KitchenPC Database Provisioning 101
As I mentioned before, KitchenPC is able to connect to a SQL database using the DBContext context type, and a configured IDBAdapter, such as the DatabaseAdapter found in KitchenPC.DB.dll. What you may not know is KitchenPC contexts are able to talk with each other, and migrate data between themselves. They can also configure their own data stores. This means DBContext can create a database for you, with the default schema, and copy the data from a StaticContext’s KPCData.xml file.
Database provisioning made easy.
So, how do we do all this? First, we need to set up two contexts. Hold on a second. Two contexts? But, I thought there could only be a single context in a process. Well, I lied. You can create as many contexts as you wish, only one can be the initialized context available through KPCContext.Current.
First, we need a database. I use PostgreSQL, so my syntax will match that.
CREATE DATABASE "KPCSample" WITH ENCODING='UTF8' OWNER="Website" CONNECTION LIMIT=-1;
Now, we have a completely blank database with no tables or anything.
Next, let’s create a DBContext and a StaticContext:
// Context connected to local database var dbConfig = Configuration.Build .Context(DBContext.Configure .Adapter(DatabaseAdapter.Configure .DatabaseConfiguration( PostgreSQLConfiguration.PostgreSQL82 .ConnectionString(@"Server=localhost;Port=5432;User Id=Website;Password=password;Database=KPCSample") .ShowSql() ) ) ).Create(); // Context connected to local data store var staticConfig = Configuration.Build .Context(StaticContext.Configure .DataDirectory(@"C:\KitchenPC\ConsoleTest\LocalStore\") ) .Create();
At this point, we have configuration builders for both contexts, and the DBContext is configured to logon to the database we just created. The StaticContext points to a directory that has KPCData.xml.
Now, we’ll add this:
var dbContext = dbConfig.Context as DBContext; dbContext.InitializeStore();
Go ahead and run your program now, just to make sure the database stuff works and what not. In theory, it should pause for a bit and then you’ll have a created database with 18 tables, all empty. If not, hopefully it threw some sort of helpful exception.
Ok, now we have a database. Which means we can populate it with data. We’ll get this data from our StaticContext. You can comment out the .InitializeStore() line since you don’t need to run it again, but if you do run it again, it will simply drop the tables and re-create them. Yes, InitializeStore() is a very dangerous method; be careful with it.
var staticContext = staticConfig.Context as StaticContext; staticContext.Initialize(); dbContext.Import(staticContext);
First, we need to intialize the StaticContext. A context must be initialized before you can import from it, since the data must be loaded into memory. We could also use KPCContext.Initialize() to do this, which in turn calls .Initialize() on the context, but we don’t really need to set a global context in this case.
The next line calls Import() on the DBContext instance. Note, we do not initialize the DBContext. Doing so would have thrown an exception, because none of the data exists for it to initialize yet. In other words, the context you’re importing the data from must be initialized so said data is available, but the context you’re importing the data into should not be initialized.
The .Import() method accepts an IProvisionSource, which both StaticContext and DBContext implement. So, you’d do the exact thing in reverse if you wanted to pull data from your database and dump it into a static XML file on disk.
When you run this, it’ll import all the data from KPCData.xml into your database. After that, you’ll be able to initialize the DBContext context and use KitchenPC.
Since this is a short post, I thought I’d throw out one more trick. The StaticContext is also able to compress its data on disk. When you initialize your StaticContext, you can say:
var staticConfig = Configuration.Build .Context(StaticContext.Configure .DataDirectory(@"C:\KitchenPC\ConsoleTest\LocalStore\") .Identity(() => new AuthIdentity(new Guid("c52a2874-bf95-4b50-9d45-a85a84309e75"), "Mike")) .CompressedStore ) .Create();
The CompressedStore flag will cause the context to look for a file called KPCData.gz instead of KPCData.xml, which is a GZip compressed version of the XML file. When StaticContext imports data, it will create a compressed file. When you initialize it, it will look for a compressed file on disk. If you configure StaticContext to use a compressed file, but only KPCData.xml exists, it will throw a file not found error. In the future, there might be a mechanism to compress a currently uncompressed file, but for now, you can just use 7-Zip or what not.