Tags: , , , , , , | Categories: ASP.Net, C#, Fluent, MVC, NHibernate, Ninject, NHibernate,Validator Posted by Admin on 9/14/2011 4:26 PM | Comments (1)

Eventually (after lots of google searches and putting together snippets from here, there and, everywhere) we now have Fluent NHibernate, NHibernate.Validator and Ninject all playing together.

First, we configure NHibernate using Fluent NHibernate passing the configuration to NHibernate.Validator so that it can configure the NHibernateSharedEngineProvider - this is important as Ninject will use the NHibernateSharedEngineProvider.

public static class Database
{
    private static ISessionFactory sessionFactory = null;

    public static ISessionFactory CreateSessionFactory()
    {
        var config = new AutoMappingConfiguration();
        ValidatorEngine validatorEngine = null;
        if (sessionFactory == null)
        {
                sessionFactory = Fluently
                .Configure()
                .Database(
                    MsSqlConfiguration
                    .MsSql2008
                    .ConnectionString(c => c.Server("").Database("").TrustedConnection()))
                .Mappings(
                    m => m.AutoMappings.Add(
                        AutoMap.AssemblyOf<EntityBase>(config)
                        .Override<EntityBase>(n => n.IgnoreProperty(c => c.ValidationErrors))
                        .Override<Country>(n => n.IgnoreProperty(c => c.DisplayName))
                        ))
                .ExposeConfiguration(c => { 
                    validatorEngine = ConfigureNHibernateValidator(c); 
                    BuildSchema(c); 
                })
                .BuildSessionFactory();
        }

        return sessionFactory;
    }

    public static ValidatorEngine ConfigureNHibernateValidator(Configuration nhibernateConfiguration)
    {
        NHibernate.Validator.Cfg.Environment.SharedEngineProvider = new NHibernateSharedEngineProvider();
        var validatorConfiguration = new NHibernate.Validator.Cfg.Loquacious.FluentConfiguration();
        validatorConfiguration
            .SetDefaultValidatorMode(ValidatorMode.UseAttribute)
            .Register(Assembly.Load("models").ValidationDefinitions())
            .IntegrateWithNHibernate
            .ApplyingDDLConstraints()
            .RegisteringListeners();
        var validatorEngine = NHibernate.Validator.Cfg.Environment.SharedEngineProvider.GetEngine();
        validatorEngine.Configure(validatorConfiguration);
        ValidatorInitializer.Initialize(nhibernateConfiguration, validatorEngine);
        return validatorEngine;
    }

    private static void BuildSchema(Configuration config)
    {
        new SchemaUpdate(config).Execute(false, true);
    }
}

Then we configure Ninject

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IEntityManager>().To<EntityManager>().InRequestScope();
    kernel.Bind<ISessionFactory>().ToMethod(x => Database.CreateSessionFactory()).InSingletonScope();
    kernel.Bind<ISession>().ToMethod(x => kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
    kernel.Bind<ISharedEngineProvider>().ToMethod(x => NHibernate.Validator.Cfg.Environment.SharedEngineProvider).InSingletonScope();
}

Then we inject both ISession and ISharedEngineProvider into our EntityManager via the .ctor. We then check the Entity is valid before saving.

public class EntityManager : IEntityManager
{
    private ISession session = null;
    private ISharedEngineProvider validator = null;

    public EntityManager(ISession session, ISharedEngineProvider validator)
    {
        this.session = session;
        this.validator = validator;
    }

    public T Save<T>(T item) where T : EntityBase
    {
        item.ValidationErrors.Clear();
        ValidatorEngine ve = this.validator.GetEngine();
        if (!ve.IsValid(item))
        {
            foreach (var invalidValue in ve.Validate(item))
            {
                item.ValidationErrors.Add(new ValidationError() { PropertyName = invalidValue.PropertyName, Message = invalidValue.Message });
            }
        }
        else
        {   
            try
            {
                this.session.SaveOrUpdate(item);
                this.session.Flush();
            }
            catch (InvalidStateException ise)
            {
                item.ValidationErrors.Clear();
                foreach (var invalidValue in ise.GetInvalidValues())
                {
                    item.ValidationErrors.Add(new ValidationError() { PropertyName = invalidValue.PropertyName, Message = invalidValue.Message });
                }
            }
        }

        return item;
    }
}

Note that within EntityManager we use ISharedEngineProvider.GetEngine() to get our pre-configured Validator. The try { ... } catch { ... } around this.Session.SaveOrUpdate(item) isn't really required but better to be safe than sorry!