Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

March 2013 - Posts

NHibernate Pitfalls: Non Nullable Columns in Table Per Class Hierarchy Inheritance

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

When you use the Table Per Class Hierarchy inheritance mapping pattern (Single Table Inheritance in Martin Fowler’s terminology), all properties of derived classes should map to nullable columns in the database. If that is not the case, it won’t be possible to insert any records of any class, because of the required columns that are not present in that class, and you will get an constraint violation error.

For example:

   1: public class PersonMapping : ClassMapping<Person>
   2: {
   3:     public PersonMapping()
   4:     {
   5:         this.Table("`PERSON`");
   6:         this.Lazy(true);
   7:  
   8:         this.Id(x => x.PersonId, x =>
   9:         {
  10:             x.Column("`PERSON_ID`");
  11:             x.Generator(Generators.HighLow);
  12:         });
  13:  
  14:         this.Property(x => x.Name, x =>
  15:         {
  16:             x.Column("`NAME`");
  17:             x.NotNullable(true);
  18:             x.Length(100);
  19:         });
  20:  
  21:         this.Property(x => x.Gender, x =>
  22:         {
  23:             x.Column("`GENDER`");
  24:             x.NotNullable(true);
  25:             x.Type<EnumType<Gender>>();
  26:         });
  27:     }
  28: }
  29:  
  30: public class NationalCitizenMappping : SubclassMapping<NationalCitizen>
  31: {
  32:     public NationalCitizenMappping()
  33:     {
  34:         this.DiscriminatorValue("NATIONAL_CITIZEN");
  35:         this.Lazy(true);
  36:  
  37:         this.Property(x => x.NationalIdentityCard, x =>
  38:         {
  39:             x.Column("`NATIONAL_IDENTITY_CARD`");
  40:             x.Length(20);
  41:             x.NotNullable(false);
  42:         });
  43:     }
  44: }
Strongly Typed Routes for ASP.NET MVC

OK, I know there are already a number of these, but since I didn’t actually know any, I wrote my own library.

I was trying to achieve something like this in a simple way:

   1: RouteTable.Routes
   2:     .ActionWithDefaultParameters<BlogController>(x => x.PostsByDate(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day))
   3:     .Map();

What we have is:

  • The controller is specified as a template parameter;
  • The action method is obtained from a strongly typed expression;
  • The default values for the action method parameters are specified in the expression.

I knew I had to start from the RouteCollection class, so I wrote a couple of extension methods:

   1: public static class RouteCollectionExtensions
   2: {
   3:     public static _Route Action<TController>(this RouteCollection routes, Expression<Func<TController, Object>> action, String url = "") where TController : IController, new()
   4:     {
   5:         MethodCallExpression method = action.Body as MethodCallExpression;
   6:  
   7:         _Route route = new _Route(routes, typeof(TController), method.Method);
   8:         route.SetUrl(url);
   9:  
  10:         return (route);
  11:     }
  12:  
  13:     public static _Route ActionWithDefaultParameters<TController>(this RouteCollection routes, Expression<Func<TController, Object>> action, String url = "") where TController : IController, new()
  14:     {
  15:         _Route route = Action<TController>(routes, action, url);
  16:         MethodCallExpression method = action.Body as MethodCallExpression;
  17:         ParameterInfo [] parameters = method.Method.GetParameters();
  18:         Expression [] arguments = method.Arguments.ToArray();
  19:  
  20:         for (Int32 i = 0; i < parameters.Length; ++i)
  21:         {
  22:             if (arguments[i] is ConstantExpression)
  23:             {
  24:                 route.AddDefaultParameterValue(parameters[i].Name, (arguments[i] as ConstantExpression).Value);
  25:             }
  26:             else if (arguments[i] is MemberExpression)
  27:             {
  28:                 route.AddDefaultParameterValue(parameters[i].Name, Expression.Lambda(arguments[i]).Compile().DynamicInvoke());
  29:             }
  30:         }
  31:  
  32:         return (route);
  33:     }
  34: }

The Action method lets you create a _Route instance with no default parameter values, while the ActionWithDefaultParameters will take any parameters in the expression (constants and simple properties only) and set them as defaults.

Next is the _Route class (sorry, I couldn’t think of a better name… Winking smile). This class implements a fluent interface for configuring the routes:

   1: public sealed class _Route
   2: {
   3:     private readonly RouteCollection routes;
   4:  
   5:     public _Route(RouteCollection routes, Type controllerType, MethodInfo actionMethod)
   6:     {
   7:         this.routes = routes;
   8:         this.ControllerType = controllerType;
   9:         this.Action = actionMethod;
  10:         this.Defaults = new RouteValueDictionary();
  11:         this.Constraints = new RouteValueDictionary();
  12:         this.Name = String.Concat(controllerType.Name.Remove(controllerType.Name.LastIndexOf("Controller")), "_", actionMethod.Name);
  13:         this.Url = String.Join("/", actionMethod.GetParameters().Select(x => String.Concat("{", x.Name, "}")));
  14:     }
  15:  
  16:     public String Url
  17:     {
  18:         get;
  19:         private set;
  20:     }
  21:  
  22:     public String Name
  23:     {
  24:         get;
  25:         private set;
  26:     }
  27:  
  28:     public RouteValueDictionary Defaults
  29:     {
  30:         get;
  31:         private set;
  32:     }
  33:  
  34:     public RouteValueDictionary Constraints
  35:     {
  36:         get;
  37:         private set;
  38:     }
  39:  
  40:     public Type ControllerType
  41:     {
  42:         get;
  43:         private set;
  44:     }
  45:  
  46:     public MethodInfo Action
  47:     {
  48:         get;
  49:         private set;
  50:     }
  51:  
  52:     public _Route SetUrl(String url)
  53:     {
  54:         if (String.IsNullOrWhiteSpace(url) == false)
  55:         {
  56:             this.Url = url;
  57:         }
  58:  
  59:         return (this);
  60:     }
  61:  
  62:     public _Route SetName(String name)
  63:     {
  64:         this.Name = name;
  65:  
  66:         return (this);
  67:     }
  68:  
  69:     public _Route AddDefaultParameterValue(String parameterName, Object value)
  70:     {
  71:         this.Defaults[parameterName] = value;
  72:  
  73:         return (this);
  74:     }
  75:  
  76:     public _Route SetOptionalParameter(String parameterName)
  77:     {
  78:         this.Defaults[parameterName] = UrlParameter.Optional;
  79:  
  80:         return (this);
  81:     }
  82:  
  83:     public void Map()
  84:     {
  85:         String url = String.Concat(this.ControllerType.Name.Remove(this.ControllerType.Name.LastIndexOf("Controller")), "/", this.Url);            
  86:         Route route = this.routes.MapRoute(this.Name, url, null, null);
  87:  
  88:         foreach (KeyValuePair<String, Object> @default in this.Defaults)
  89:         {
  90:             route.Defaults[@default.Key] = @default.Value;
  91:         }
  92:  
  93:         foreach (KeyValuePair<String, Object> constraint in this.Constraints)
  94:         {
  95:             route.Constraints[constraint.Key] = constraint.Value;
  96:         }
  97:  
  98:         route.Defaults["Controller"] = this.ControllerType.Name.Remove(this.ControllerType.Name.LastIndexOf("Controller"));
  99:         route.Defaults["Action"] = this.Action.Name;
 100:     }
 101:  
 102:     /*
 103:     
 104:     */
 105:  
 106:     public _Route AddConstraints(Object constraints)
 107:     {
 108:         PropertyDescriptorCollection props = TypeDescriptor.GetProperties(constraints);
 109:  
 110:         foreach (PropertyDescriptor prop in props)
 111:         {
 112:             this.AddDefaultParameterValue(prop.Name, prop.GetValue(constraints));
 113:         }
 114:  
 115:         return (this);
 116:     }
 117:  
 118:     public _Route AddConstraint<TRouteConstraing>() where TRouteConstraing : IRouteConstraint, new()
 119:     {
 120:         this.Constraints[Guid.NewGuid().ToString()] = new TRouteConstraing();
 121:  
 122:         return (this);
 123:     }
 124:  
 125:     public _Route AddConstraint(IRouteConstraint constraint)
 126:     {
 127:         this.Constraints[Guid.NewGuid().ToString()] = constraint;
 128:  
 129:         return (this);
 130:     }
 131:  
 132:     public _Route AddConstraint(String parameterName, String regularExpression)
 133:     {
 134:         this.Constraints[parameterName] = regularExpression;
 135:  
 136:         return (this);
 137:     }
 138:  
 139:     public _Route AddConstraint(String parameterName, IRouteConstraint constraint)
 140:     {
 141:         this.Constraints[parameterName] = constraint;
 142:  
 143:         return (this);
 144:     }
 145: }

It has the following methods:

  • AddConstraint: for adding route constraints (several overloads);
  • AddDefaultParameterValue/SetOptionalParameter: for setting default parameter values;
  • SetName: for setting a name for the route;
  • SetUrl: for setting the URL;
  • Map: for actually registering the route.

SetName and SetUrl are optional, because it will try to create a URL from the action method parameters and will name the route like Controller_Action.

Some more usages:

   1: RouteTable.Routes
   2:     .ActionWithDefaultParameters<BlogController>(x => x.GetPostsForDate(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day))
   3:     .AddConstraint<DateRouteConstraint>()
   4:     .Map();
   5:  
   6: RouteTable.Routes
   7:     .Action<AnotherController>(x => x.SomeAction(null))
   8:     .SetOptionalParameter("someParameter")
   9:     .Map();
  10:  
  11: RouteTable.Routes
  12:     .Action<HomeController>(x => x.Index())
  13:     .SetName("Default")
  14:     .Map();

Hope you enjoy!

Soft Deletes With NHibernate, Part II

After my previous post, Adam Bar suggested that setting an SQL Delete command might be a better idea, I actually agree with him, so I decided to post an alternative solution.

For those not familiar with the concept, NHibernate lets us specify SQL commands for all of the basic CRUD operations: loading, inserting, updating and deleting. The SQL can even be a stored procedure call. In this case, it’s the deleting that we’re after, and it is quite easy to configure:

   1: public class RecordMapping : ClassMapping<Record>
   2: {
   3:     public RecordMapping()
   4:     {
   5:         this.Table("record");
   6:         this.Lazy(true);
   7:         this.Where("deleted = 0");
   8:         this.SqlDelete("UPDATE record SET deleted = 0 WHERE record_id = ?");
   9:  
  10:         this.Id(x => x.RecordId, x =>
  11:         {
  12:             x.Column("record_id");
  13:             x.Generator(Generators.HighLow);
  14:         });
  15:  
  16:         this.Version(x => x.UpdatedAt, x =>
  17:         {
  18:             x.Column(y =>
  19:             {
  20:                 y.NotNullable(true);
  21:                 y.Name("updated_at");
  22:             });
  23:             x.Generated(VersionGeneration.Never);
  24:             x.Type(NHibernateUtil.UtcDateTime as IVersionType);
  25:         });
  26:         
  27:         this.Property(x => x.CreatedAt, x =>
  28:         {
  29:             x.Column("created_at");
  30:             x.NotNullable(true);
  31:             x.Update(false);
  32:             x.Insert(true);
  33:         });
  34:  
  35:         this.Property(x => x.CreatedBy, x =>
  36:         {
  37:             x.Column("created_by");
  38:             x.NotNullable(true);
  39:             x.Update(false);
  40:             x.Insert(true);
  41:         });
  42:  
  43:         this.Property(x => x.UpdatedBy, x =>
  44:         {
  45:             x.Column("updated_by");
  46:             x.NotNullable(true);
  47:         });
  48:  
  49:         this.Property(x => x.Name, x =>
  50:         {
  51:             x.Column("name");
  52:             x.Length(50);
  53:             x.NotNullable(true);
  54:         });
  55:  
  56:         this.Property(x => x.Deleted, x =>
  57:         {
  58:             x.Column("deleted");
  59:             x.NotNullable(true);
  60:         });
  61:  
  62:         this.ManyToOne(x => x.Parent, x =>
  63:         {
  64:             x.Column("parent_record_id");
  65:             x.NotNullable(false);
  66:             x.Lazy(LazyRelation.NoProxy);
  67:             x.Cascade(Cascade.All);
  68:         });
  69:  
  70:         this.Bag(x => x.Children, x =>
  71:         {
  72:             x.Inverse(true);
  73:             x.Cascade(Cascade.All | Cascade.DeleteOrphans);
  74:             x.SqlDelete("UPDATE record SET deleted = 1 WHERE record_id = ?");
  75:             x.Where("deleted = 0");
  76:             x.Key(y =>
  77:             {
  78:                 y.Column("parent_record_id");
  79:                 y.NotNullable(false);
  80:             });
  81:         }, x =>
  82:         {
  83:             x.OneToMany();
  84:         });
  85:     }
  86: }

Notice the two calls to SqlDelete, one for the class itself, and the other for the Children collection. The former takes care of soft deleting the record itself, and the later, it’s children.

It is a more contained and straightforward solution, no need for registering listeners.

Attaching Disconnected Entities in NHibernate Without Going to the Database

Because the SaveOrUpdateCopy method of ISession is marked as obsolete, we no longer have an API method that allows us to attach a disconnected entity (meaning: coming from a session that no longer exists) to a new session without going to the database, something that Merge, Lock, Refresh all do. Sometimes this is not desirable.

One thing I love about NHibernate is how extensible it is! It was a piece of cake to build this extension method:

   1: public static T Attach<T>(this ISession session, T entity, LockMode mode = null)
   2: {
   3:     mode = mode ?? LockMode.None;
   4:  
   5:     IEntityPersister persister = session.GetSessionImplementation().GetEntityPersister(NHibernateProxyHelper.GuessClass(entity).FullName, entity);
   6:     Object[] fields = persister.GetPropertyValues(entity, session.ActiveEntityMode);
   7:     Object id = persister.GetIdentifier(entity, session.ActiveEntityMode);
   8:     EntityEntry entry = session.GetSessionImplementation().PersistenceContext.AddEntry(entity, Status.Loaded, fields, null, id, null, LockMode.None, true, persister, true, false);
   9:     
  10:     return (entity);
  11: }

A simple example:

   1: Product product = null;
   2:  
   3: using (ISession session = sessionFactory.OpenSession())
   4: {
   5:     product = session.Query<Product>().First();
   6: }
   7:  
   8: using (ISession session = sessionFactory.OpenSession())
   9: {
  10:     session.Attach(product);
  11:  
  12:     product.Price = 34230;
  13:  
  14:     session.Flush();
  15: }
Soft Deletes With NHibernate

Updated: thanks, Adam

Sometimes we are not allowed to physically delete records from a database. This may be because of some policy, legal obligations, traceability, or whatever. It is sometimes called “soft deleting”. NHibernate offers an elegant solution for this problem.

So, what we want to achieve is:

  • We will use a DELETED column to indicate if a record is deleted (1) or not (0);
  • Only records with DELETED = 0 will be retrieved from the database;
  • If we mark a record for deletion, it will have its DELETED column updated instead.

First, let’s define some interface that characterizes soft deletable entities:

   1: public interface ISoftDeletable
   2: {
   3:     Boolean Deleted
   4:     {
   5:         get;
   6:         set;
   7:     }
   8: }

And a specific implementation:

   1: public class Record : ISoftDeletable
   2: {
   3:     public Record()
   4:     {
   5:         this.Children = new List<Record>();
   6:     }
   7:  
   8:     public virtual Int32 RecordId
   9:     {
  10:         get;
  11:         set;
  12:     }
  13:  
  14:     public virtual String Name
  15:     {
  16:         get;
  17:         set;
  18:     }
  19:  
  20:     public virtual IList<Record> Children
  21:     {
  22:         get;
  23:         protected set;
  24:     }
  25:  
  26:     public virtual Record Parent
  27:     {
  28:         get;
  29:         set;
  30:     }
  31:  
  32:     #region ISoftDeletable Members
  33:  
  34:     public virtual Boolean Deleted
  35:     {
  36:         get;
  37:         set;
  38:     }
  39:  
  40:     #endregion
  41:  
  42:     public override String ToString()
  43:     {
  44:         return (this.Name);
  45:     }
  46: }

Let’s map this using mapping by code:

   1: public class RecordMapping : ClassMapping<Record>
   2: {
   3:     public RecordMapping()
   4:     {
   5:         this.Table("`RECORD`");
   6:         this.Lazy(true);
   7:         this.Where("DELETED = 0");
   8:  
   9:         this.Id(x => x.RecordId, x =>
  10:         {
  11:             x.Column("`RECORD_ID`");
  12:             x.Generator(Generators.HighLow);
  13:         });
  14:  
  15:         this.Property(x => x.Name, x =>
  16:         {
  17:             x.Column("`NAME`");
  18:             x.Length(50);
  19:             x.NotNullable(true);
  20:         });
  21:  
  22:         this.Property(x => x.Deleted, x =>
  23:         {
  24:             x.Column("`DELETED`");
  25:             x.NotNullable(true);
  26:         });
  27:  
  28:         this.ManyToOne(x => x.Parent, x =>
  29:         {
  30:             x.Column("`PARENT_RECORD_ID`");
  31:             x.NotNullable(false);
  32:             x.Lazy(LazyRelation.NoProxy);
  33:             x.Cascade(Cascade.All);
  34:         });
  35:  
  36:         this.Bag(x => x.Children, x =>
  37:         {
  38:             x.Inverse(true);
  39:             x.Cascade(Cascade.All | Cascade.DeleteOrphans);
  40:             x.Where("DELETED = 0");
  41:             x.Key(y =>
  42:             {
  43:                 y.Column("`RECORD_ID`");
  44:                 y.NotNullable(false);
  45:             });
  46:         }, x =>
  47:         {
  48:             x.OneToMany();
  49:         });
  50:     }
  51: }

Did you notice the Where calls on both class and Children properties? Well, they tell NHibernate that a specific restriction will be appended whenever an instance of the Record class is loaded, either directly or through the Children collection (another option would be filters). So, with this, we have set up a loading filter.

What we need now is interception of ISession.Delete calls, to use the Deleted property instead of actually removing the record. We achieve this by using a listener for the PreDelete event:

   1: public class SoftDeletableListener : IPreDeleteEventListener
   2: {
   3:     public void Register(Configuration cfg)
   4:     {
   5:         cfg.EventListeners.PreDeleteEventListeners = new IPreDeleteEventListener[] { this }.Concat(cfg.EventListeners.PreDeleteEventListeners).ToArray();
   6:     }
   7:  
   8:     #region IPreDeleteEventListener Members
   9:  
  10:     public Boolean OnPreDelete(PreDeleteEvent @event)
  11:     {
  12:         ISoftDeletable softDeletable = @event.Entity as ISoftDeletable;
  13:  
  14:         if (softDeletable == null)
  15:         {
  16:             return(true);
  17:         }
  18:  
  19:         EntityEntry entry = @event.Session.GetSessionImplementation().PersistenceContext.GetEntry(@event.Entity);
  20:         entry.Status = Status.Loaded;
  21:  
  22:         softDeletable.Deleted = true;
  23:  
  24:         Object id = @event.Persister.GetIdentifier(@event.Entity, @event.Session.EntityMode);
  25:         Object [] fields = @event.Persister.GetPropertyValues(@event.Entity, @event.Session.EntityMode);
  26:         Object version = @event.Persister.GetVersion(@event.Entity, @event.Session.EntityMode);
  27:  
  28:         @event.Persister.Update(id, fields, new Int32[1], false, fields, version, @event.Entity, null, @event.Session.GetSessionImplementation());
  29:  
  30:         return (true);
  31:     }
  32:  
  33:     #endregion
  34: }

And that’s about it. We use it like this:

   1: Configuration cfg = ...;
   2:  
   3: SoftDeletableListener listener = new SoftDeletableListener();
   4: listener.Register(cfg);
   5:  
   6: ISessionFactory sessionFactory = ...;
   7:  
   8: using (ISession session = sessionFactory.OpenSession())
   9: {
  10:     //only non-deleted records will be retrieved
  11:     Record record = session.Query<Record>().First();
  12:     IEnumerable<Record> children = record.Children.ToList();
  13:  
  14:     session.Delete(record);
  15:     session.Flush();    //an UPDATE will be issued instead of a DELETE
  16: }

As always, hope this is useful to someone! Winking smile

PS - As Adam Bar pointed out, there is another way, which uses sql-delete, and is easier to set up. There's a discussion on StackOverflow, here.

NHibernate Pitfalls: Making Changes to the Configuration After the Session Factory Is Built

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

You shouldn’t be making changes to the configuration instance after the session factory is built, because it won’t know about these changes. These include:

  • Adding listeners;
  • Registering named queries;
  • Creating filters.

For example, these won’t work:

   1: Configuration cfg = new Configuration();
   2:  
   3: using (ISessionFactory sessionFactory = cfg.BuildSessionFactory())
   4: {
   5:     //adding a named query
   6:     cfg.NamedQueries["ReceivedOrders"] = new NamedQueryDefinition("from Order o where o.State = 0", true, null, 20, 0, FlushMode.Never, true, "Received orders", null);
   7:  
   8:     //creating a filter
   9:     cfg.AddFilterDefinition(new NHibernate.Engine.FilterDefinition("CurrentLanguage", "code = :code", new Dictionary<String, IType>() { { "code", NHibernateUtil.String } }, false));
  10:  
  11:     //registering a listener
  12:     cfg.EventListeners.PreDeleteEventListeners = new IPreDeleteEventListener[] { new SoftDeletableListener() }.Concat(cfg.EventListeners.PreDeleteEventListeners).ToArray();
  13: }

Of course, new session factories created after applying the changes will work as expected.

My All Time Favorite Posts

Since this blog started, back in 2008, I wrote a lot of posts. I’d say some are still up to date. I picked a few of them, those I’m more proud of, in no particular order.

ASP.NET Web Forms:

ASP.NET MVC:

NHibernate:

.NET:

Let me know what you think of them! Are there others you particularly enjoyed?

NHibernate Pitfalls: Integrating NHibernate Validator

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

NHibernate Validator is the standard way to implement custom validation of entities – that is, validation other than just based on the table schema definition. It is available from Nuget (https://nuget.org/packages/NHibernate.Validator) and as source code from GitHub (https://github.com/darioquintana/NHibernate-Validator).

This post is not about implementing custom validation. For that, I recommend the excellent collection of posts by Fabio Maulo and Dario Quintana.

This is about the actual integration of Validator with NHibernate; it has some gotchas that you should be aware of. Here’s a simple guide.

Let’s suppose you have a User class:

   1: public class User
   2: {
   3:     public virtual Int32 Id { get; protected set; }
   4:     public virtual String Name { get; set; }
   5:     public virtual String Email { get; set; }
   6:     public virtual DateTime Birthday { get; set; }
   7: }

You want to be sure that:

  • The Name and Email properties have values and have at most 50 characters;
  • The Email property is a valid email address;
  • The Birthday is in the past.

Let’s forget about the mappings, they should be very easy to create. Let’s focus instead on NHibernate Validator, and, following what seems the current trend on .NET, loquacious (or fluent) configuration:

   1: FluentConfiguration validatorConfiguration = new FluentConfiguration();
   2: ValidationDef<User> userValidation = new ValidationDef<User>();
   3: userValidation.Define(x => x.Birthday).IsInThePast().WithMessage("The birthday must be in the past")
   4: .And
   5: .Define(x => x.Email).NotNullableAndNotEmpty().WithMessage("The email is mandatory")
   6: .And
   7: .MaxLength(50).WithMessage("The email can only have 50 characters")
   8: .And
   9: .IsEmail().WithMessage("The email must be a valid email adddress")
  10: .And
  11: .Define(x => x.Name).NotNullableAndNotEmpty().WithMessage("The name is mandatory")
  12: .And
  13: .MaxLength(50).WithMessage("The email can only have 50 characters");
  14:  
  15: ;

You can see that be created a validation configuration instance and we populated it with a validation definition for the User class. You could as well create your own class that inherits from ValidationDef<User> and place all validation logic there.

Next, we need to integrate NHibernate Validator with NHibernate:

   1: validationConfiguration.SetDefaultValidatorMode(ValidatorMode.UseExternal)
   2: .IntegrateWithNHibernate
   3: .ApplyingDDLConstraints()
   4: .RegisteringListeners();
   5:  
   6: NHibernate.Validator.Cfg.Environment.SharedEngineProvider = new NHibernateSharedEngineProvider();
   7: ValidatorEngine validatorEngine = NHibernate.Validator.Cfg.Environment.SharedEngineProvider.GetEngine();
   8: validatorEngine.Configure(validatorConfiguration);            
   9:  
  10: //cfg is the NHibernate Configuration instance
  11: cfg.Initialize(validatorEngine);

If you are curious, NHibernate Validator will add listeners for the PreInsert and PreUpdate events, which will be fired when the session is about to save or update some entity.

And that’s it! Now, validation will occur in two distinct ways:

  • Explicit, if you call ValidatorEngine.Validate:
   1: User u = new User();
   2: InvalidValue[] invalidValuesObtainedExplicitly = validatorEngine.Validate(u);
  • Implicit, if you try to save an invalid entity:
   1: ISession session = ...;
   2:  
   3: try
   4: {
   5:     User u = new User();
   6:     session.Save(u);
   7:     session.Flush();
   8: }
   9: catch (InvalidStateException ex)
  10: {
  11:     InvalidValue [] invalidValuesObtainedFromException = ex.GetInvalidValues();
  12: }

Using a shared engine provider (the NHibernate.Validator.Cfg.Environment.SharedEngineProvider instance) is required, because the listeners have no other way of getting your configured ValidatorEngine. You are free – and advised to, actually – to create your own provider, by implementing NHibernate.Validator.Event.ISharedEngineProvider in the way that best suits your needs, perhaps using IoC to retrieve a single instance.

NHibernate Pitfalls: Batch Loading

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

NHibernate allows setting a batch size for some entity or to collections of some entity. The entity setting applies to lazy loaded many to one or one to one associations, and the collection applies to lazy loaded one to many or many to many collections. This is something that might improve the performance of your application.

In the first case, what it does is, if there are more than one loaded entities of some type with some lazy loaded association which wasn’t already loaded, and one tries to access one such association property, triggering its loading, then up to batch-size identical associations will be fetched at the same time, on the loaded entities of the same type. For example:

   1: Order o1 = session.Get<Order>(1);
   2: Order o2 = session.Get<Order>(2);
   3: Order o3 = session.Get<Order>(3);
   4:  
   5: //Customer is lazy loaded and its batch size is set to 3
   6: Customer c1 = o1.Customer;    //the 3 Customer instances will be loaded at the same time, not just the one from o1
   7: //SELECT … FROM [Customer] WHERE [Id] IN (@p0, @p1, @p2)

This may be regarded as as optimization, because, if you are certain that you will need all Customer properties, then all of them are loaded when the first is accessed.

The same thing happens with collections:

   1: Order o1 = ...;
   2: Order o2 = ...;
   3: Order o3 = ...;
   4:  
   5: //OrderDetails is lazy loaded and its batch size is set to 3
   6: Int32 c1 = o1.OrderDetails.Count();    //the 3 OrderDetails collections will be loaded at the same time, not just the one from o1

However, this may not be what you expect, because it might return a lot of data, and there’s a chance you wont even use it.

If you are not certain, disable batching by not setting a batch size either at the entity and the collection level. Be aware, however, of its consequences, and consider it wisely.

More Posts