IEditableObject: another behaviour for your NHibernate entities

First of all you need to read my previous post and this from Fabio Maulo (a Italian guy who knows how to play truco better than me).
This time the behaviour is related to the IEditableObject. This interface has three methods BeginEdit, CancelEdit and EndEdit. Microsoft says:

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

Note: it says “commit or rollback changes to an object”, not to a database.

Can we do it with a castle IInterceptor? YES.

The interceptor is defined as follows:

public class EditableObjectInterceptor : IInterceptor
   {
       private bool _isEditing;
       private readonly Dictionary<string, object> _propertyTempValues 
                                   = new Dictionary<string, object>();

       public void Intercept(IInvocation invocation)
       {
           switch (invocation.Method.Name)
           {
               case "BeginEdit":
                   _isEditing = true;
                   return;
               case "CancelEdit":
                   _isEditing = false;
                   return;
               case "EndEdit":
                   _isEditing = false;
                   AssignValues(invocation.InvocationTarget);
                   return;
           }
           if (!_isEditing)
           {
               invocation.Proceed();
               return;
           }
           if(!invocation.Method.Name.StartsWith("set_") 
               && !invocation.Method.Name.StartsWith("get_"))
               return;

           var isSet = invocation.Method.Name.StartsWith("set_");
           var propertyName = invocation.Method.Name.Substring(4);

           if(isSet)
           {
               _propertyTempValues[propertyName] = invocation.Arguments[0];
           }
           else
           {
               if (_propertyTempValues.ContainsKey(propertyName))
                   invocation.ReturnValue = _propertyTempValues[propertyName];
               else
                   invocation.Proceed();
           }
       }

       private void AssignValues(object target)
       {
           foreach (var propertyTempValue in _propertyTempValues)
           {
               var property = target.GetType().GetProperty(propertyTempValue.Key);
               property.SetValue(target, propertyTempValue.Value, null);
           }
       }
   }

If you use CommonDatastore (explained in my previous post) you add this behavior as follows:

container.Register(Component.For<IProduct>()
                            .AddEditableObjectBehavior()
                            .TargetIsCommonDatastore());

If you don’t use CommonDatastore:

container.Register(Component.For<Customer>()
                            .AddEditableObjectBehavior()
                            .EnableNhibernateEntityCompatibility());

Let the test speak for me:

        [Test]
        public void property_setters_should_work()
        {
            var product = container.Resolve<IProduct>();
            product.Description = "Potatoes";
            product.Description.Should().Be.EqualTo("Potatoes");

            product.BeginEdit();
            product.Description = "Banana";
            product.Description.Should().Be.EqualTo("Banana");
            product.Description = "Apple";
            product.Description.Should().Be.EqualTo("Apple");
            product.CancelEdit();
        }

        [Test]
        public void cancel_should_discard_changes_in_trascientobject()
        {
            var product = container.Resolve<IProduct>();
            product.Description = "Potatoes";
            product.Description.Should().Be.EqualTo("Potatoes");

            product.BeginEdit();
            product.Description = "Banana";
            product.Description.Should().Be.EqualTo("Banana");
            product.CancelEdit();

            product.Description.Should().Be.EqualTo("Potatoes");

        }

        [Test]
        public void endedit_should_push_changes_in_trascientobject()
        {
            var product = container.Resolve<IProduct>();
            product.Description = "Potatoes";
            product.Description.Should().Be.EqualTo("Potatoes");

            product.BeginEdit();
            product.Description = "Banana";
            product.EndEdit();

            product.Description.Should().Be.EqualTo("Banana");

        }

        [Test]
        public void cancel_should_discard_changes()
        {
            var id = CreatePotatoesProduct();
            using(var session = sessions.OpenSession())
            using(var tx = session.BeginTransaction())
            {
                var product = session.Get<IProduct>(id);

                product.Description.Should().Be.EqualTo("Potatoes");

                product.BeginEdit();
                product.Description = "Banana";
                product.Description.Should().Be.EqualTo("Banana");
                product.CancelEdit();

                product.Description.Should().Be.EqualTo("Potatoes");
            }
        }

        [Test]
        public void endedit_should_push_changes()
        {
            var id = CreatePotatoesProduct();
            using (var session = sessions.OpenSession())
            using (var tx = session.BeginTransaction())
            {
                var product = session.Get<IProduct>(id);

                product.Description.Should().Be.EqualTo("Potatoes");

                product.BeginEdit();
                product.Description = "Banana";
                product.Description.Should().Be.EqualTo("Banana");
                product.EndEdit();
                product.Description.Should().Be.EqualTo("Banana");
            }
        }
kick it on DotNetKicks.com

blog comments powered by Disqus
  • Categories

  • Archives