in
Forums
Blogs
Files
Devexpress.Com
ClientCenter
Support Center
DevExpress Channel

XPO

eXpress Persistent Objects

May 2007 - Posts

  • Receiving SqlDependency notifications for use outside XPO

     

    Well, there's obviously one really easy way to do it: just hook into the SqlDependency feature independently from XPO and receive the notifications that way. But there's an easier way to do it if XPO is hooking these notifications anyway. I got some code from an XPO developer and I created a sample around it to show how it works.

    Note: Before that developer agreed to give me his code, he made me promise to say this: we really don't recommend you use this! It is an approach that will have severe performance drawbacks in many multi-user scenarios. If you end up actually using it, please make sure you do testing on your application in circumstances that resemble production use. If you find that you have performance problems - which we believe is very likely -, we will not be able to do anything about it on the XPO level. Please consider yourself warned.

    Now you're probably asking: "why is he going to post a sample if he doesn't want me to use it?" Well, I believe that it is better to explain things to people, including (potentially) bad things, and let them make up their own minds. So here we go.

    The sample I'm using is the same I showed in the previous post about the SqlDependency feature. You can download the whole source code below. For the purpose of notification, the sample now includes a new class called DataStoreChangeTrackingWrapper, which has two important (and a bunch of not as important) methods:

    	...
    	protected void ProcessChanges(DataCacheResult result) {
    		CurrentCookie = result.Cookie;
    		if (result.UpdatedTableAges == null)
    			OnEverythingChanged( );
    		else if (result.UpdatedTableAges.Count > 0)
    			OnTablesChanged(result.UpdatedTableAges.Keys);
    	}
    	...
    	public void CatchUp( ) {
    		DataCacheResult result = Root.NotifyDirtyTables(CurrentCookie);
    		ProcessChanges(result);
    	}
    	...

    The DataStoreChangeTrackingWrapper class is a data store implementation. It overrides the ProcessUpdateSchema, ProcessSelectData and ProcessModifyData methods and makes sure that ProcessChange is called during the execution of all these methods. So if the application accesses the data store regularly (like the sample application does in its loop), the OnEverythingChanged and OnTablesChanged methods are going to be called automatically. The CatchUp method is for those cases where the regularity is not there - a timer could be used to call into CatchUp regularly to make sure that updates are passed through.

    The two methods OnEverythingChanged and OnTablesChanged are those that get called once changes have been detected in the database. OnTablesChanged is the more common of the two, and it gets more detailed information as well. OnEverythingChanged is called only in cases where the cache gets disconnected - for instance when the cache root service is stopped in the case of a remotely published cache root. 

    If you run the sample application and follow the same steps as in the previous article, you should see that at the point where external changes are being discovered, information about them will appear in the console. The sample uses a 5 second sleep in the main loop (previously one second), so that it's easy to spot the additional output line.

    Download the sample here: XPOSqlServer2005CacheAndNotification2.zip (4640 bytes)

  • XPO Beta feature: SqlDependency support

    SqlDependency is a SQL Server 2005 feature that allows the database server to notify a client when changes occur in the database. XPO already has a strong caching infrastructure, but up until now it didn't have support for the SqlDependency feature. We will introduce this support in our DXperience 7.2 release, and the recent 7.1.3 release includes the feature as a beta. Please go ahead and test it and let us know of any problems you may find, but don't rely on its stability until 7.2 is released.

    Prerequisites

    To make the SqlDependency feature work, there are some requirements that have to be kept. Microsoft has all the information about this on MSDN.

    A test program

    To demonstrate how the feature in XPO works, I have created a small test program. It starts out like this (I left out the Person class - it's a standard persistent class, please download the source from below if you need it):

    static void Main(string[] args) {
    	XpoDefault.DataLayer =
    		new SimpleDataLayer(new DataCacheNode(new DataCacheRoot(
    		XpoDefault.GetConnectionProvider(
    		MSSqlConnectionProvider.GetConnectionString(
    		"(local)", "XPOSqlServer2005CacheAndNotification"),
    		AutoCreateOption.DatabaseAndSchema))));
    
    	using (Session initSession = new Session( )) {
    		initSession.UpdateSchema(typeof(Person), typeof(XPObjectType));
    	}
    
    	for (; ; ) {
    		using (UnitOfWork uow = new UnitOfWork( )) {
    			Console.Clear( );
    			XPCollection people = new XPCollection(uow);
    			if (people.Count > 0)
    				foreach (Person p in people)
    					Console.WriteLine("Key: {0} Person: {1}", p.Oid, p.Name);
    			else
    				Console.WriteLine("Empty collection");
    			Thread.Sleep(1000);
    		}
    	}
    }
    

    As you can see, the program simply sets up a cached connection provider, creates all the table structures for the Person class, and then goes into a loop, querying again and again for all the Person instances in the database. Now, of course I am interested to see what queries actually go through to the database, so I also activate logging in the App.config (see here for the background on that):

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    	<system.diagnostics>
    		<switches>
    			<add name="XPO" value="3" />
    		</switches>
    	</system.diagnostics>
    </configuration>
    

    Now I run the application, and of course there's nothing in the database. The console shows "Empty collection" and the log output is also restricted to the minimum - note how the queries against the database are run just once, as the caching layer catches all the rest.

     

    Now, the interesting thing here is that when I go and add a record to the Person table in SQL Server Management Studio (while leaving the application running), the change will not be picked up by the application. So effectively the application is suddenly running with invalid data, although it's requerying all the time, and even using new sessions as well. This is the problem that the SqlDependency feature can solve.

     

    Using SqlDependency

    Using the SqlDependency support requires just one change to the source code of the test program: instead of calling the standard GetConnectionString method on the MSSqlConnectionProvider class, I use the method GetConnectionString2005WithCache instead. Because the connection provider that is created under these circumstances includes caching by default, I don't need to create the DataCacheNode and DataCacheRoot classes myself. So the line that sets the data layer looks like this:

    	XpoDefault.DataLayer =
    		new SimpleDataLayer(XpoDefault.GetConnectionProvider(
    			MSSqlConnectionProvider.GetConnectionString2005WithCache(
    			"(local)", "XPOSqlServer2005CacheAndNotification"),
    			AutoCreateOption.DatabaseAndSchema));
    

    Now when I rerun the application, I still get the caching behaviour, so the log output in VS looks much the same as before. The difference is that as soon as a record is added from SQL Server Management Studio, the change is picked up automatically. The next XPCollection that the test program creates results in an actual database query and the changed data is correctly retrieved. I can see in the VS log output how the query is executed - just once, as needed.

     

    Publishing the service

    It is important to consider that the SqlDependency feature uses resources on the server. Basically, every time a change happens in a table that is being monitored, every subscribed client must be notified. In conjunction with XPO, it seems advisable to make use of the publishing infrastructure, for instance via .NET Remoting, so that only one subscriber to the server's change notification exists. For this purpose there's another helper method on the MSSqlConnectionProvider class, called GetConnectionString2005CacheRoot. Using GetConnectionProvider with a connection string returned by GetConnectionString2005CacheRoot results in an object that implements ICachedDataStore. When this object is published and a client connects to it using a connection string of http://host:port/servicename or tcp://host:port/servicename format, XPO creates a DataCacheNode on the client automatically. The result is a setup that still benefits from change notifications on the database server, but doesn't have great resource requirements.

    As you have seen, the creation of objects that activate the SqlDependency feature is based on the connection strings. Incidentally, my own XPO publication service also uses connection strings in its configuration file, so it is compatible with this new feature. A connection string that would be returned by GetConnectionString2005CacheRoot would look like this, for example: XpoProvider=MSSqlServer2005CacheRoot;data source=(local);integrated security=SSPI;initial catalog=XPOSqlServer2005CacheAndNotification

    Download

    Here's the test program I created, if you want to try out everything for yourself: XPOSqlServer2005CacheAndNotification.zip (4109 bytes)

  • ANN: DXCore XPO plugin 1.1.0.5

    Here's the newest version - bug fixes, no new functionality. If you're not familiar with the purpose of the XPO plugin, please read this description of the "Simplified Criteria Syntax" feature.

    This is the download for version 1.1.0.5: CR_XPOFieldSync-1.1.0.5.zip (20432 bytes)

    Version 1.1.0.5 is compiled against DXCore 2.2.2, so it won't work with older versions. Here are the fixes:

    • Fixed CB44938 - wrong format of FieldsClass when deriving from generic class.
    • Fixed a problem where a property of the FieldsClass would get an invalid type if the corresponding property in the persistent class had a type that was a generic type parameter.
    • Fixed a bug where painting exceptions would be thrown while working in generic persistent classes.
    • Fixed CB46287 - XPCollection<T> as return type of properties breaks property generation in FieldsClass
    • Fixed CB46287 - Another problem with XPCollection as a property type
    • Fixed CB47358 - Persistent classes as property types lead to incomplete generation.

    The issue of CB8627 is still outstanding, as described previously. As usual, if you have any other problems with the plugin, please let us know!

Copyright © 1998-2008 Developer Express Inc.
ALL RIGHTS RESERVED