FluentValidation

by MikeHogg 29. March 2013 20:56

I've always been kind of disappointed in my model class files in MVC after the first or second phase of a project, because the properties end up with so many DataAnnotation attributes that it's just easier to read an intellisense popup to see the class properties than view the code file directly.  And this is just with simple Display and Validation Attributes.

DisplayAttributes always seemed wrong to me just on principle.  It is putting Presentation Layer stuff in Domain Model files.  I always have to show the front end developers where this ‘default’ display attribute can be changed, and they are not used to looking at or modifying c# code.  For this reason, I’m torn on whether to continue to use Display attributes automatically.  But to help simplify my Model files, I would rather pull what model properties I need for my particular view out into ViewModel files, and put the attributes there. 

Validation Attributes are also tied to the Presentation, or User Input Layer.  Several times it happens to me that one page requires some validation, and another page using the same model, does not require validation (usually because it’s in read-only state on that page) and then I have to work around the validators.  These also belong in VM files.  So your Required Validators are only called where they are used.  Now your Model classes are clean, and should pretty closely resemble your data objects.

But- Validation isn’t strictly front end or presentation layer stuff, and so for further simplification, jump on the Fluent bandwagon, and take all those Validation attributes out of your ViewModel files and put them in FluentValidator classes.  It's really easy to set up, and the benefits you get are similar to Activity Based Authorization, in that all of your validation stuff is in exactly one place, and isn't interlaced in the display code of dozens of other presentation files.  Your Validation is now loosely coupled to both your presentation layer and your domain layer, and your code is easier to read and maintain.

Add the FluentValidationMVC to your NuGet, and let it pull in the dependencies.  Add exactly one line to your Application_Start

FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();

and then put the attribute on your VM class itself,

[FluentValidation.Attributes.Validator(typeof(QuestionOptionValidator))]

And then create the validator class, and you can just enter all the validations, and nothing but the validations...

    public class QuestionOptionValidator : FluentValidation.AbstractValidator<QuestionOption>
    {
        public QuestionOptionValidator()
        {
            RuleFor(x => x.QuestionID).GreaterThan(0).When(qo => qo.ID != 0);
            RuleFor(x => x.ChartDisplay).NotNull();
            RuleFor(x => x.SpanishChartDisplay).NotEmpty().WithMessage("Please enter a Spanish Chart");
            RuleFor(x => x.CurrentlyViewedEventID).Must((q, currentlyviewedeventid) => q.IsActive && currentlyviewedeventid > 0);
            RuleFor(x => x.Description).Matches("^Desc.*$");
 
        }
         
    }

I don't need to paste examples, their docs are great.

http://fluentvalidation.codeplex.com/wikipage?title=Validators&referringTitle=Documentation&ANCHOR#Predicate

Tags:

MVC | Fluent

NHibernate midstream

by MikeHogg 7. March 2013 14:30

NHibernate is an active ORM product, and has been one for some time.   Coming to it as I have, at version 3 can be difficult to digest, especially as you are soaking up new syntax, even if the patterns are not unfamiliar.  Especially since there are many options and support libraries to choose from, and the core library has changed so much from version one to two and again to three. 

So just blindly googling lines of code and quickstarts can easily get you in lots of dark woods unless you pay attention to the dates of web posts, and understand how NH evolved over time. 

First, the patterns.  NH works from a Repository pattern by using db sessions.  Inject the NH SessionFactory into your repositories and then your Repos can run all the NH Query goodness instead of you writing a whole bunch of db layer stuff.  In its simplest form, you can use an NHHelper class to statically create a SessionFactory inside your Repo, or you can use Construction Injection and let your Injection Library do that for you automatically, like here with Ninject

public class NHibernateSessionFactory
    {
        public ISessionFactory GetSessionFactory()
        { //later }
        public class NHibernateSessionFactoryProvider : Ninject.Activation.Provider<ISessionFactory>
        {
            protected override ISessionFactory CreateInstance(Ninject.Activation.IContext context)
            {
                var sessionFactory = new NHibernateSessionFactory();
                return sessionFactory.GetSessionFactory();
            }
        }

 

then your Repo looks something like this:

 

 public class Repository<T> :  IRepository<T> where T:class
    {
        private readonly ISession session;
 
        public Repository(ISession s)
        {
            session = s;
        }
 

So GetSessionFactory is where all the NH code is config'd.

Here are your options for configuring...

Originally, everything was XML and config based, so you would have the base NH.config() in your code, and your web.config would have a section with the required and optional properties in it.

 

 
public ISessionFactory GetSessionFactory()
        {
            var config = new Configuration();
            config.BuildSessionFactory();
 

 

Then your web.config would have all the hookup:

 

 <configSections>
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectio
nHandler, NHibernate" />
</configSections>
  <connectionStrings>
    <add name="CONN" connectionString="blahblah" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
      <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
      <property name="connection.connection_string_name">CONN</property>
      <property name="show_sql">true</property>
    </session-factory>
  </hibernate-configuration>
 

Then came Fluent, and config was moved into code. so, if you use that library, you no longer need the web.config and you can just use this in your SessionFactory...

 

        
public ISessionFactory GetSessionFactory()
        {
            // fluent style config.  We use both to apply loquacious mapping by code to old style config, and then create FluentConfiguration with it
            FluentNHibernate.Cfg.FluentConfiguration fluentConfiguration = FluentNHibernate.Cfg.Fluently.Configure()
                                                   .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ShowSql()
                                                    .ConnectionString(c => c.FromConnectionStringWithKey("CONN")))
                                                    // only need one From Assembly to point to all the maps These are for Fluent MAPS inherting from ClassMap
                                                   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Maps.EventMap>())                
                                                   .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                   .ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"));                                       
                                                    //.ExposeConfiguration(BuildSchema) 
 
            return fluentConfiguration.BuildSessionFactory();

In addition to config, when setting up your NH Environment, you need to init your maps.  If you already have database tables, the first order of business is to get NMG, NHibernateMappingGenerator.  This reads the db and outputs entity classfiles and classmaps and is a huge help.  Note in the screenshot below, in the upper right hand side of the preferences, how you can choose which format of Mapping files you want.

 

Originally, these would be XML files, (convention) named something.hbm.xml.  We no longer need to do that.  You see also FluentNHibernate and the new Loquacious format.  FNH uses ClassMap parent class, and I believe that might be the convention that allows AddAssemblyFromMaps<>() to work.  Loquacious uses ClassMapping parent, and I believe offers more flexibility in filtering types from Assembly to map.

With NH 3 the project merged the Loquacious (Mapping By Code) codebase that was previously a library, but it is not very mature so a lot of posts I have seen are where it is being used for simpler project requirements or in tandem with a large base of existing Fluent mappings.  I could not figure out a way to add MBC maps to FluentMappings, so in case you ever want to do that here is code to initialize and map both ways, using a base NH.Config with web.config for MBC maps and building your FluentConfig with that base, adding FluentMaps in that process...

 
public ISessionFactory GetSessionFactory()
        {
            
            // old style config 
            var mm = new NHibernate.Mapping.ByCode.ModelMapper();
            Type[] mappingTypes = typeof(Maps.EventMap).Assembly.GetExportedTypes().Where(t => t.Name.EndsWith("Map")).ToArray();
            mm.AddMappings(mappingTypes);
 
            var config = new Configuration(); 
            config.AddMapping(mm.CompileMappingForAllExplicitlyAddedEntities());
 
 
            // fluent style config.  We use both to apply loquacious mapping by code to old style config, and then create FluentConfiguration with it
            FluentNHibernate.Cfg.FluentConfiguration fluentConfiguration = FluentNHibernate.Cfg.Fluently.Configure(config)
                                                   .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ShowSql()
                                                    .ConnectionString(c => c.FromConnectionStringWithKey("CONN")))
                                                    // only need one From Assembly to point to all the maps These are for Fluent MAPS inherting from ClassMap
                                                   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Maps.EventMap>())                
                                                   .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                   .ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"));                                       
                                                    //.ExposeConfiguration(BuildSchema) 
 
            return fluentConfiguration.BuildSessionFactory();
        }

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more. amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here