Archives

Archives / 2013 / March
  • 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: }

    Read more...

  • 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!

    Read more...

  • 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.

    Read more...

  • 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: }

    Read more...

  • 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.

    Read more...

  • 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?

    Read more...

  • 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.

    Read more...