Introducing uNhAddIns.ComponentBehaviors

In this post I will introduce a project under Unofficial NHibernate Addins named ComponentBehaviors. The goal of this project is to inject some goodness of System.ComponentModel through a dynamic proxy framework.

The WHY

Sometimes you need to implement certain interfaces of ComponentModel, for various reasons such as DataBinding, but the code need to accomplish this is tedious and repetitive. This code is called "plumbing code", an the following snippet is an example:

public class Customer : BaseEntity
{
    private string _firstName;
    public virtual string FirstName
    {
        get { return _firstName; }
        set
        {
            if(!_firstName.Equals(value))
            {
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }
    }

    private string _lastName;
    public virtual string LastName
    {
        get { return _lastName; }
        set
        {
            if(!_lastName.Equals(value))
            {
                _lastName = value;    
                OnPropertyChanged("LastName");
            }
        }
    }
}

In the above sample, a property declaration took me 12 lines of code. Why I can't use auto properties (1 line)?

My goal was to have the same functionality like the above but with the following code:

public class Customer
{
    public virtual string FirstName {get; set;}
    public virtual string LastName {get; set;}
}

//Somewhere

configuration.For<Customer>()
    .Add<NotifiableBehavior>()
    .Add<DataErrorInfoBehavior>()
    .Add<EditableBehavior>();

A side effect is that classes are not tied to those interfaces and can be used in another project.

The WHAT

By now there are three supported behaviors:

  • DataErrorInfoBehavior
  • EditableBehavior
  • NotifyPropertyChangedBehavior

DataErrorInfo Behavior

The DataErrorInfo behavior is based on the interface IDataErrorInfo, and is described by me in this post. The configuration is as follows:

configuration.For<Customer>()
.Add<DataErrorInfoBehavior>();

You need to plug in your container an IEntityValidator, fortunately we have four implementations in uNhAddIns:

  • NHibernate Validator
  • DataAnnotations
  • Application Validation Block
  • Castle Validations

You can read more in this post of Fabio Maulo.

IDataErrorInfo is described in MSDN :

Provides the functionality to offer custom error information that a user interface can bind to.

Almost any DataBinding infrastructures such as Asp.Net, Asp.Net MVC, Winforms and WPF supports binding to an IDataErrorInfo entity.

Editable Behavior

The Editable behavior is based on the interface IEditableObject. The goal of this behavior is described in MSDN as follows:

Provides functionality to commit or rollback changes to an object that is used as a data source.

Note: Commit or Rollback refers to in-memory operations. Not database.

So a test like this, is self-explanatory:

[Test]
public void session_shouldnot_be_dirty_after_cancelchanges()
{
    var id = CreateNewAlbum();
    using (ISession session = sessions.OpenSession())
    using (ITransaction tx = session.BeginTransaction())
    {
        var album = session.Get<Album>(id);
        ((IEditableObject) album).BeginEdit();
        album.Title = "Dark side of the moon";
        ((IEditableObject) album).CancelEdit();
        session.IsDirty().Should().Be.False();

        tx.Commit();
    }
}

NotifyPropertyChanged behavior

NotifyPropertyChanged behavior is based on the interface INotifyPropertyChanged and is very well described in the MSDN:

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

The WHERE

With uNhAddIns.ComponentBehaviors you define the behaviors of a class ONCE and you can get an entity with the behaviors in many ways. I will describe in this section how ComponentBehaviors works with NHibernate.

Transient Entities

We just need an IEntityFactory in our projects as follows:

public class EntityFactory : IEntityFactory
{
    private readonly IComponentProxyFactory _componentProxyFactory;

    public EntityFactory(IComponentProxyFactory componentProxyFactory)
    {
        _componentProxyFactory = componentProxyFactory;
    }

    public T CreateInstance<T>()
    {
        return (T) _componentProxyFactory.GetEntity(typeof (T));
    }
}

Just plug that in your container and now you will be able to inject an IEntityFactory in your services.

Persisted entities

There are two ways to tell NHibernate that every time we return an object, this must come with the behaviors. The first way is with an interceptor as follows:

nhibernateConfig.Interceptor = 
    container.Resolve<ComponentBehaviorInterceptor>();

The second is has a little overhead because it involves to inject your entities in the container and now I'm using vaguely. But you can read more in this thread.

Proxy entities

With the steps described in the previous section, whenever nhibernate return an object, this object will come with the behaviors. But what happen when NHibernate itself need to generate a proxy for lazyloading stuff? Well, we can even specify that we want our behaviors in those proxies.

And the configuration is very easy:

cfg.Properties[Environment.ProxyFactoryFactoryClass] = 
    typeof (ComponentProxyFactoryFactory)
        .AssemblyQualifiedName

 

Final thoughts

If you want to see a working applications that use those concepts, have a look to this example.

I’ve started this project because I need it for my job, this was my first serious experience developing open source and for me has been very positive:

  • I’ve talked with lot of people around the globe, open source developers or not.
  • Some of them are using the library and improving.
  • I’ve learned a lot of Castle DynamicProxy and NHibernate.
  • I’ve participated in the afore mentioned projects.
  • I’ve three new job offers.

A word about NHibernate

For me, one of the interesting things that I found in NHibernate is a lot of “extensions points”. You could plug whatever you want and radically change the behavior of the tool to fit your needs.

Enjoy the NHibernate injection. :-)


blog comments powered by Disqus
  • Categories

  • Archives