XPO and ASP.NET - a look at the initialization code

XPO Team Blog
20 July 2007

Mehul got back to me yesterday and asked me to explain the XPO related initialization code that is being used in our new ASPxGridView demo. This is the code in question:

    void Application_Start(object sender, EventArgs e) 
{
if(DemoSettings.IsSiteMode) {
ConnectionStringSettings conn =
ConfigurationManager.ConnectionStrings["XPOServerMode"];
if(conn != null) {
// Code that runs on application startup
DevExpress.Xpo.Metadata.XPDictionary dict =
new DevExpress.Xpo.Metadata.ReflectionDictionary();
dict.GetDataStoreSchema(typeof(ServerSideGridTest).Assembly);
DevExpress.Xpo.DB.IDataStore store =
DevExpress.Xpo.XpoDefault.GetConnectionProvider(
conn.ConnectionString,
DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);
store = new DevExpress.Xpo.DB.DataCacheNode(
new DevExpress.Xpo.DB.DataCacheRoot(store));
object layer = new DevExpress.Xpo.ThreadSafeDataLayer(dict, store);
Application.Add("XpoLayer", layer);
}
}
}

Great. Now what's happening here? Let's go over the method line by line:

    if(DemoSettings.IsSiteMode) { 
ConnectionStringSettings conn =
ConfigurationManager.ConnectionStrings["XPOServerMode"];

These are demo specific lines, nothing to do with XPO. In fact, when working with XPO it is a common practice to use helper methods on certain XPO classes to retrieve an auto-generated connection string instead of using a pre-created one. For SQL Server, for example, you'd want a call into DevExpress.Xpo.DB.MSSqlConnectionProvider.GetConnectionString( ... ); to get a connection string. There's nothing wrong with the approach of storing away the connection string, but it's certainly a little easier to break.

    DevExpress.Xpo.Metadata.XPDictionary dict = 
new DevExpress.Xpo.Metadata.ReflectionDictionary();

The dictionary of metadata that XPO works with contains important information about the persistent classes in your application, and it is associated with the so-called data layer (an object that implements IDataLayer, if you want to be technical about it). In general, a data layer is only created once per application, so the metadata dictionary is also created just once.

In a Windows Forms application the code related to configuring the data layer is typically much simpler, probably something like this:

    XpoDefault.DataLayer = XpoDefault.GetDataLayer( ... );

In ASP.NET, things are not quite as simple because we have to deal with the fact that we're working in a multi-threaded environment. If you take a little peak ahead in the complete code of the Application_Start method above, you will see a class called ThreadSafeDataLayer being instantiated. This specific implementation of IDataLayer can work in multi-threaded applications, but it requires a metadata dictionary that is stable during the application's lifetime. A data layer in a Windows Forms (single threaded) application would be extended during the application run, depending on the classes that are being used. This is not allowed to happen in the ASP.NET multi-threaded scenario, and that's why we have to create and fill the metadata dictionary manually, and before creating the data layer.

    dict.GetDataStoreSchema(typeof(ServerSideGridTest).Assembly); 

This line does the work: it needs an assembly reference (or several actually, it is possible to pass in more than one assembly reference at once), finds all the metadata in that assembly and adds it to the dictionary. To get to the correct assembly reference, one of the persistent types in the application is used to find the reference dynamically. This is an arbitrary persistent type in your application -- it looks a bit a hack, but it's one of the easiest ways of getting hold of such a reference.

    DevExpress.Xpo.DB.IDataStore store = 
DevExpress.Xpo.XpoDefault.GetConnectionProvider(
conn.ConnectionString,
DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);

Now we are ready to create a connection provider object, which is an object that implements the interface IDataStore. This is basically the DB specific driver that XPO needs to communicate with your backend of choice. The XpoDefault.GetConnectionProvider( ... ) method analyzes the connection string to find out which type of connection provider it needs to create. For connection strings that are retrieved from the XPO helper methods (see above), there's a special flag inserted into the connection string that simplifies identification. In the ASPxGridView demo, the connection string is read from the Web.config file, but it still contains the flag. Here's the string (wrapped):

    XpoProvider=MSSqlServer;data source=(local);user id=servermodeprojects;
password=smp$;initial catalog=ServerModeGridProjects;Persist Security Info=true

The first parameter XpoProvider tells the GetConnectionString method that it needs to instantiate the connection provider for MS SQL Server in this case. If your connection string doesn't have such a flag, there are heuristics in that method trying to recognize the string nevertheless, but this may not work totally reliably. You should either retrieve the connection string from one of the helper methods or make sure the flag is in there if you store the complete connection string in a config file.

Finally, there's an enum AutoCreateOption being passed into that method. This tells XPO how to handle the database schema - SchemaAlreadyExists says that the schema shouldn't be created, because it is expected to be there already. There are several other alternatives documented in the XPO product documentation.

    store = new DevExpress.Xpo.DB.DataCacheNode( 
new DevExpress.Xpo.DB.DataCacheRoot(store));

This line wraps the IDataStore in instances of DataCacheRoot and DataCacheNode. DataCacheNode implements IDataStore itself, so the result of the operation can be used anywhere a "normal" connection provider can also be used. The purpose of the wrapping is to establish data layer caching, as described in this technical article.

    object layer = new DevExpress.Xpo.ThreadSafeDataLayer(dict, store);

With the metadata dictionary and the connection provider in hand, we can finally create our thread-safe data layer. I don't know why the person who wrote the demo used object as the type -- it should really be something explicit, must likely IDataLayer.

    Application.Add("XpoLayer", layer); 

This line of code stores the data layer away for later use. Apart from the ASP.NET specific approach used here, there's also a mechanim built into XPO to set a default data layer: the static XpoDefault.DataLayer property. The main difference is that if XpoDefault.DataLayer has been set, it is no longer necessary to pass in an explicit data layer reference when constructing instances of Session or UnitOfWork. Of course there's the general argument that this works only as long as just one data layer is being used in an application, but that is really very common -- as I hear, the approach was chosen in this case becauses use of the Application class for this kind of thing is regarded as a good practice in ASP.NET applications. Wouldn't necessarily convince me, but there you go.

1 comment(s)
Anonymous
The ASPx Blog

In the DotNetRocks show #246 on ASP.NET scalability, Stephen Forte mentions one of the biggest problems

20 November, 2007

Please login or register to post comments.