RecentComments

Comment RSS

NHibernate, PropertyChanged event and WPF

by Ioannis 28. August 2009 15:30

A typical implementation of an entity that supports the INotifyPropertyChanged interface for WPF Binding is for example as follows:

public class Detail:INotifyPropertyChanged
{

    private long _iD;
    public virtual long ID
    {
        get { return _iD; }
        set { _iD = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("ID"));}
    }
 
    private string _line1;
    public virtual string Line1
    {
        get { return _line1; }
        set { _line1 = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("Line1"));}
    }
 
    private string _line2;
    public virtual string Line2
    {
        get { return _line2; }
        set { _line2 = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("Line2"));}
    }
 
    private Entity _relatedEntity;
    public virtual Entity RelatedEntity
    {
        get { return _relatedEntity; }
        set { _relatedEntity = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("RelatedEntity"));}
    }
    public virtual event PropertyChangedEventHandler PropertyChanged;

 

Class Detail supports the INotfiyPropertyChanged interface and is ready for data-binding in a WPF Window. Try to bind the properties "Line1" and "Line2" to TextBoxes in a WPF Window and soon you will realize that if you load the entity from a database using NHibernate by:

 

  • Load<Detaill>(id) : The PropertyChanged events are ignored by WPF
  • Get<Detail>(id): The PropertyChanged events are processed as expected by WPF

This behaviour is due to the fact that Load returns a proxy object while Get doesn't. More seriously though if in the same window you want to bind to properties of the property RelatedEntity (eg RelatedEntity.Description)  which is instantiated by a many-to-one relationship and you use "Lazy-Loading", you will have a proxied Entity object and the PropertyChanged event will be ignored as before.

It seems that when a proxy is involved, ProperyChanged events are ignored by WPF.The obvious choice is to always use Get and disable "Lazy-Loading" which is of course something that usually is not an option.

But there seems to be another solution:

The reason the problem occurs has to do with the fact that WPF expects a PropertyChanged event from the proxy object and gets it from the actual object. This can be better explained with the following figure:

 

When the property "Line1" is changed(Bottom), a PropertyChanged event is generated that goes through the proxy to the WPF TextBox. WPF ignores the event since it is databound to the _detailProxy object and not to the _detail object. In other words it awaits events of the form PropertyChanged (_detailProxy,"Line1") and not PropertyCahnged (_detail,'Line1").

The solution is to extend the proxy implementaion so that it takes the resoonsibility of notifying WPF when a property is changed. We will keep the original implementation of the entity as is to support the NotifyPropertyChanged mechanism when NHibernate is not involved in the object creation process and we will create a new Interceptor for the proxy to support the same functionality when NHibernate creates the object for us. Moreover the process will be as non invasive as possible in case in future implementations of NHibernate the issue is resolved.

We create a new file called NotifyProprtyChangedInterceptor.cs and first, we add a bunch of .using statements (make sure you reference the appropriate libraries):

using System.ComponentModel;
using NHibernate.Proxy.Poco.Castle;
using System.Reflection;
using NHibernate.Engine;
using NHibernate.Type;
using Castle.Core.Interceptor;
using Castle.DynamicProxy.Generators;
 
using System.Collections;
using NHibernate.Proxy;
using NHibernate;

We implement in it the following class which will create our extended proxy:

// We override this class implementation to handle the proxy creation ourselves. We
// need to specify our own Interceptor when a method of our entity is called.
public class DataBindingNotifyPropertyProxyFactory : CastleProxyFactory
{
    public override INHibernateProxy GetProxy(object id, ISessionImplementor session)
    {
        // If it is not a proxy for a class do what you usually did.
        if (!IsClassProxy) return base.GetProxy(id, session);
 
        try
        {               
            CastleLazyInitializer initializer = new DataBindingInterceptor(EntityName,PersistentClass, id,
                       GetIdentifierMethod, SetIdentifierMethod,ComponentIdType, session);
           
            // Add to the list of the interfaces that the proxy class will support the INotifyPropertyChanged interface.
            // This is only needed in the case when we need to cast our proxy object as INotifyPropertyChanged interface.
            ArrayList list = new ArrayList(Interfaces);
            list.Add(typeof(INotifyPropertyChanged));
            System.Type[] interfaces = (System.Type[])list.ToArray(typeof(System.Type));
 
            //We create the proxy (for the class, Interfaces Supported, Interceptor to use)
            object generatedProxy = DefaultProxyGenerator.CreateClassProxy(PersistentClass, interfaces, initializer);
 
            initializer._constructed = true;
            return (INHibernateProxy)generatedProxy;
        }
        catch (Exception e)
        {
            log.Error("Creating a proxy instance failed", e);
            throw new HibernateException("Creating a proxy instance failed", e);
        }
    }
}

In the same file we implement the interceptor that is used by the proxy:

public class DataBindingInterceptor : CastleLazyInitializer
 {
     private PropertyChangedEventHandler subscribers = delegate { };
 
     public DataBindingInterceptor(String EntityName,Type persistentClass, object id,MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod,IAbstractComponentType aType, ISessionImplementor session)
         : base(EntityName,persistentClass, id, getIdentifierMethod, setIdentifierMethod,aType, session)
     {
     }
 
     public override void Intercept (IInvocation invocation)
     {
         // WPF will call a method named add_PropertyChanged to subscribe itself to the property changed events of
         // the given entity. The method to call is stored in invocation.Arguments[0]. We get this and add it to the
         // proxy subscriber list.
         if (invocation.Method.Name.Contains("PropertyChanged"))
         {
             PropertyChangedEventHandler propertyChangedEventHandler = (PropertyChangedEventHandler)invocation.Arguments[0];
             if (invocation.Method.Name.StartsWith("add_"))
             {
                 subscribers += propertyChangedEventHandler;
             }
             else
             {
                 subscribers -= propertyChangedEventHandler;
             }
         }
        
         // Here we call the actual method of the entity
         base.Intercept(invocation);
 
         // If the method that was called was actually a proeprty setter (set_Line1 for example) we generate the
         // PropertyChanged event for the property but with event generator the proxy. This must do the trick.
         if (invocation.Method.Name.StartsWith("set_"))
         {              
             subscribers(invocation.InvocationTarget, new PropertyChangedEventArgs(invocation.Method.Name.Substring(4)));
         }
     }
 }

Finally we implement the creator of the proxy factory in the same file:

public class ExtendedWithNotifyProxyFactoryFactory :NHibernate.Bytecode.IProxyFactoryFactory
{
    public IProxyFactory BuildProxyFactory()
    {
        return new DataBindingNotifyPropertyProxyFactory();
    }
}

And we are done! To use the new interceptor, when inititalizing NHibernate we add the following (line needed in red color):

Configuration cfg = new Configuration();
cfg.AddAssembly("NHibernateTests");
cfg.Properties[NHibernate.Cfg.Environment.ProxyFactoryFactoryClass] = typeof(ExtendedWithNotifyProxyFactoryFactory).AssemblyQualifiedName;
_sessionFactory = cfg.Configure().BuildSessionFactory();

Now we can work with proxies and WPF receives the appropriate PropertyChanged events when properties change. Whenever we want to disable this functionality we just omit the line that adds it in the configuration.

If you just need to grab the code and keep working download the following .cs file, add it to your project, add the References and the configuration in the previous paragraph (in red) and you are good to go:

NotifyPropertyChangedInterceptor.zip

This solution-code was based and insipired by the following articles:

Castle Dynamic Proxy tutorial by Krzysztof Koźmic
Extending NHibernate Proxies by Ayende Rahien
The discussion started by Arthur Dorochowicz

kick it on DotNetKicks.com

Shout it

Tags:

.NET | WPF

Timers in WPF

by Ioannis 31. July 2009 07:44

There are times when you need a  task to execute periodically. There are two ways of achieving this:

  • Use the System.Windows.Threading.DispatcherTimer
  • Use the System.Threading.Timer

Say now that you want to implement a class the executes DoSomething() periodically and also informs through a delegate to whoever listens whent the execution is performed. The class for DispatcherTimer is as follows:

public class DTimer
{
    private DispatcherTimer timer;
    public event Action<int> DoSomething;
 
    private int _timesCalled = 0;
 
    public DTimer()
    {
        timer=new DispatcherTimer();
    }
    public void Start(int PeriodInSeconds)
    {
        timer.Interval = TimeSpan.FromSeconds(PeriodInSeconds);
        timer.Tick += timer_Task;
        _timesCalled = 0;
        timer.Start();
    }
 
    public void Stop()
    {
        timer.Stop();
    }
    private void timer_Task(object sender, EventArgs e)
    {
        _timesCalled++;
        DoSomething(_timesCalled);
 
    }
}

The same functionality for Timer is provided by the class below:

 

public class TTimer
{
 
    private Timer timer;
    public event Action<int> DoSomething;
    private int _timesCalled = 0;
 
    public TTimer()
    {}
    public void Start(int PeriodInSeconds)
    {
        timer = new System.Threading.Timer(timer_Task, null, 0,
        PeriodInSeconds*1000);
    }
    public void Stop()
    {
        timer.Dispose();
    }
    private void timer_Task(object State)
    {
        _timesCalled++;
        DoSomething(_timesCalled);
    }
}
Is there any difference? Well one important one is the fact that DoSomething() in the case of the DispatcherTimer is called in the same thread as the application while DoSomething() in the case of Timer is called in a new thread that was created for the timer. The two important implications of this are:
  • If DoSomething() manipulates GUI components then with the Timer you need to use: this.Dispatcher.Invoke((Action)delegate { //GUI RELATED CODE HERE} since you cannot access GUI controls from a different thread directly. With DispatcherTimer you do not need to do that.
  • If DoSomething() performas a time-consuming task, then the GUI will freeze in the case of the DispatcherTimer. In the case of the Timer it won't since the long methos is executed in a different thread

kick it on DotNetKicks.com

Shout it

Tags:

.NET | WPF

Domain Driven Design, Business Objects, Datasets, NHibernate and friend Jack (Part 2/2)

by Ioannis 12. February 2009 19:59

 

In the previous post, we have discussed some issues that emerged from a conversation with my friend Jack. In this post we will explore a little bit his following statement:

Jack: Well(…) I have created a BO (a simple one, mapped to a single table in the DB) and created a very simple DataLayer using NHibernate to retrieve a list of 1500 BOs and display them in a DataGrid. It took NHibernate 6 sec to retrieve the data! Can you believe it? (…) I have tried the same thing with a DataSet and it took only some msec (…)  I tried to find a solution to this in the internet for 3 hours and found nothing. NHibernate is poorly documented too… Nothing beats DataSets.

This needs proof. This needs code. So I have implemented a small application as my testbench to perform my measurement with NHibernate. The TestBench is as follows:

We create a small DB in MS SQL with a single table and 20000 records:

As you can see the table consists of a Primary Key (ID – Bigint) and 5 columns with strings (String1..5) of type nvarchar(50).

Now we go to Visual Studio, create a new application and implement the Business Object as follows (this is what Jack did):

 

That is we provide a Property and a Field for every column in the Table. The FirePropertyChangedNotification(value) method is at the parent GenericBO class and is implemented as follows:

Which is the typical BO’s support of the INotifyPropertyChanged interface needed for updating the GUI correctly in data binding scenarios.

The next thing was to set up NHibernate and provide the mapping file between the table and the BO:

Now it was time to perform some tests. I decided to test several ways of retrieving the list of TestClass BOs both with NHibernate Session and SessionStateless. So the tests are as follows (all tests return List<TestClass> unless explicitly specified):

 

I got the following times (for test 3 to work I needed to specify a property ID named as the column in the Table in TestClass whose accessors are accessing the field _primaryKey) :

  Test 1 Test 2 Test 3 Test 4
1st Time 26,078sec 23,927 sec 23,113 sec 23,735 sec
2nd Time 0,117 sec 0,098 sec 23,050 sec 0,106 sec
Times include time to display in WPF DataGrid

The results are disappointing. Of course the second time, since there is no change in the Cache the times are very low (in Test3 the times are the same for the first and second time since we are performing a plain mapping with a transformer) but what happens in the first Time? Will we have better lack if we perform the same tests in a StateLess session?

  Test 1 Test 2 Test 3 Test 4
1st Time 25,573sec 24,890sec 24,531sec N/A
2nd Time 25.041 sec 25,029 sec 23,427sec N/A
Times include time to display in WPF DataGrid

Now we are worse off. First and Second time are huge due to the absence of the cache. Was Jack right after all? Before jumping into conclusions let’s enable logging for NHibernate and see the log:

Retrieval starts at:

2009-02-11 21:41:05,268 [10] DEBUG NHibernate.Loader.Loader - processing result set
2009-02-11 21:41:05,268 [10] DEBUG NHibernate.Loader.Loader - result set row: 0
2009-02-11 21:41:05,274 [10] DEBUG NHibernate.Loader.Loader - result row: EntityKey[ORMTests.TestClass#1]
2009-02-11 21:41:05,279 [10] DEBUG NHibernate.Loader.Loader - Initializing object from DataReader: [ORMTests.TestClass#1]
2009-02-11 21:41:05,285 [10] DEBUG NHibernate.Persister.Entity.AbstractEntityPersister - Hydrating entity: [ORMTests.TestClass#1]
 

First phase ends at:

2009-02-11 21:41:11,949 [10] DEBUG NHibernate.Loader.Loader - result set row: 19999
2009-02-11 21:41:11,949 [10] DEBUG NHibernate.Loader.Loader - result row: EntityKey[ORMTests.TestClass#20000]
2009-02-11 21:41:11,949 [10] DEBUG NHibernate.Loader.Loader - Initializing object from DataReader: [ORMTests.TestClass#20000]
2009-02-11 21:41:11,949 [10] DEBUG NHibernate.Persister.Entity.AbstractEntityPersister - Hydrating entity: [ORMTests.TestClass#20000]
2009-02-11 21:41:11,949 [10] DEBUG NHibernate.Loader.Loader - done processing result set (20000 rows)

First phase takes 6 seconds !

Second phase starts at:

2009-02-11 21:41:11,957 [10] DEBUG NHibernate.Engine.TwoPhaseLoad - resolving associations for [ORMTests.TestClass#1]
2009-02-11 21:41:11,962 [10] DEBUG NHibernate.Engine.TwoPhaseLoad - done materializing entity [ORMTests.TestClass#1]

Second phase ends at:

2009-02-11 21:41:32,601 [10] DEBUG NHibernate.Engine.TwoPhaseLoad - resolving associations for [ORMTests.TestClass#20000]
2009-02-11 21:41:32,602 [10] DEBUG NHibernate.Engine.TwoPhaseLoad - done materializing entity [ORMTests.TestClass#20000]

Second phase takes 21 seconds !

With a little research it seems that in the second phase NHibernate performs the mapping of the raw values to the Business Objects. To verify that we run Test 3 again but this time we do not map the results to BOs: 

 

Now the time has decreased to : 1 sec !

There must be a delay when mapping the data to the BOs. So I have manually added the code to map the data to the TestClass Business Objects just after getting the List<object> ListReturned as follows: 

 

The retrieval time returned to 15,227sec which is an improvement from 23sec but still not acceptable.

So the problem is not with NHibernate but rather it is in the way we have implemented the BO. And the first suspicious place where this may be happening is when calling the FirePropertyChangedNotification(…). So we first remove from the function the automatic retrieval of the Property’s name which may cause the delay:

(StateLess Session)

  Test 1 Test 2 Test 3 Test 4
1st Time 8,855sec 8,827sec 4,651sec N/A
2nd Time 8,638sec 8,815sec 4,170sec N/A
Times include time to display in WPF DataGrid

We have 50% increase in performance. How about removing the method call completely and putting its behavior directly in the properties?

(StateLess Session)

  Test 1 Test 2 Test 3 Test 4
1st Time 0,813sec 0,510sec 0,331sec N/A
2nd Time 0,490sec 0,553sec 0,320sec N/A
Times include time to display in WPF DataGrid

Which is now acceptable. To make sure we try to load the same data using a DataSet which gives approximately 0,301sec.

The last thing to do is perform the comparison between Test2 and DataSets for increasing table size:

   

Apparently there is a difference in DataSets but now this difference is justified since NHibernate is an ORM tool which is more than a simple mapping tool which creates the DataSets.

As a conclusion, Jack was again wrong. Wrong, because it was his implementation that lead to this big stall in performance and not NHibernate itself. Moreover, when he was searching the internet for a solution it was normal not to be able to find one since the issue was caused by his way of implementing things.

My effort is not to advertise NHibernate. I am sure that the same would emerge with EDM or any other ORM tool. The point of this 2 post discussion is three-fold:

  • First to show a way of thinking which in my opinion can lead to overstatements.
  • Second to provide a general overview of DDD, ORM, DataSets and Business Objects.
  • Third to show some performance analysis results when using NHibernate and a way to resolve issues about those.

 

kick it on DotNetKicks.com

Tags:

.NET | Databases

Powered by BlogEngine.NET 1.5.0.7

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

Ioannis Panagopoulos


NDepend is a tool that simplifies managing a complex .NET code base. Architects and developers can analyze code structure, specify design rules, plan massive refactoring, do effective code reviews and master evolution by comparing different versions of the code.

NDepend Tool



This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add