Recently Krzysztof Koźmic has committed a patch to the DynamicProxy trunk that allows me to do a new trick.
I will show you in this post a beautiful way to add behaviour to your entities.
I start defining this simple domain:
public interface IProduct { int Id { get; } string Name { get; set; } double UnitPrice { get; set; } } public class Product : IProduct { public int Id { get; set; } public string Name { get; set; } public double UnitPrice { get; set; } } public interface IEditableProduct : IProduct, INotifyPropertyChanged, IEditableObject {}
Then I could generate a proxy as follows:
public IEditableProduct GenerateEditableModelOfProduct(Product product) { var proxyGen = new ProxyGenerator(); return (IEditableProduct) proxyGen.CreateInterfaceProxyWithTargetInterface(typeof (IProduct), new[] {typeof (IEditableProduct)}, product, new ProxyGenerationOptions(), new IInterceptor[] { new EditableObjectInterceptor(), new PropertyChangeInterceptor() } ); }Let me improve it by adding generics:
public TEditableInterface GenerateEditableModel<TEditableInterface, TDomainInterface>(TDomainInterface entity) where TEditableInterface : INotifyPropertyChanged, IEditableObject, TDomainInterface { var proxyGen = new ProxyGenerator(); return (TEditableInterface) proxyGen.CreateInterfaceProxyWithTargetInterface(typeof(TDomainInterface), new[] {typeof (TEditableInterface)}, entity, new ProxyGenerationOptions(), new IInterceptor[] { new EditableObjectInterceptor(), new PropertyChangeInterceptor() } ); }
Note: you can take EditableObjectInterceptor and PropertyChangeInterceptor from unhaddins.examples.
And now the test:
[Test] public void TestEditableBehaviour() { var product = new Product() { Name = "Potatoes" }; var editableProductModel = GenerateEditableModel<IEditableProduct, IProduct>(product); editableProductModel.Name.Should().Be.EqualTo("Potatoes"); editableProductModel.BeginEdit(); editableProductModel.Name = "Apple"; editableProductModel.Name.Should().Be.EqualTo("Apple"); editableProductModel.CancelEdit(); editableProductModel.Name.Should().Be.EqualTo("Potatoes"); editableProductModel.BeginEdit(); editableProductModel.Name = "Succory"; editableProductModel.Name.Should().Be.EqualTo("Succory"); editableProductModel.EndEdit(); editableProductModel.Name.Should().Be.EqualTo("Succory"); }
[Test] public void TestNotificableBehaviour() { var product = new Product() { Name = "Potatoes" }; bool eventWasCalled = false; var editableProductModel = GenerateEditableModel<IEditableProduct, IProduct>(product); editableProductModel.PropertyChanged += (sender, args) => { if (args.PropertyName == "Name") eventWasCalled = true; }; editableProductModel.UnitPrice = 2.2; eventWasCalled.Should().Be.False(); editableProductModel.Name = "Succory"; eventWasCalled.Should().Be.True(); }
If you read my two previous post you will find a way to work with NHibernate as follows:
Session.Get<IProduct>(1);
//or:
Session.Get<IEditableProduct>(1);
