Browse NHibernate metadata to validate property Length

One of the most basic validation rule for Entities that are stored in databases, is to be sure that String Properties are not too long for the corresponding field in database. There are a lot of validation framework over there, most of them using attributes to specify constraints on properties or fields, but what happens if someone changes a mapping setting a different length for a field?

I’m a great fan of database generation with NHibernate, so here is a typical mapping of mine.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                         assembly="DotNetMarche.Validator.Tests"
                         namespace="DotNetMarche.Validator.Tests.Extras.NHibernate">

    <class name="EntityBase" table="EntityBase" lazy="false">
        <id name="Id" column="id" type="System.Int32" unsaved-value="0">
            <generator class="native" />
        </id>

        <property name="MyName" column="code" type="System.String" length="50"/>
        <property name="AnotherProperty" column="name" type="System.String"/>
    </class>

</hibernate-mapping>

This is a class for unit testing a specific routine, it has a property called MyName that have length specified in the mapping, now I want to create a Validator that is able to automatically create rules from the mapping. The goal is automatically find maximum string length from mapping, thus avoiding the extra work to create attributes for each property, and moreover if you change the mapping, the rule changes accordingly. Here is a routing that does this .

public static Core.Validator GetValidatorFromSession(ISessionFactory sessionFactory)
{
    var allDefindedClasses = sessionFactory.GetAllClassMetadata();
    Core.Validator validator = new Core.Validator();
    foreach (KeyValuePair<string, IClassMetadata> pair in allDefindedClasses)
    {
        IClassMetadata metadata = pair.Value;
        foreach (string propertyName in metadata.PropertyNames)
        {
            IType propertyType = metadata.GetPropertyType(propertyName);
            StringType st = propertyType as StringType;
            if (st != null)
            {
                if (st.SqlType.Length > 0)
                {
                    validator.AddRule(Rule.For(metadata.GetMappedClass(EntityMode.Poco))
                        .OnMember(propertyName)
                          .MaxLength(st.SqlType.Length)
                          .Message(String.Format(
                                "Property {0} have a maximum length of {1}", 
                                    propertyName,
                                      st.SqlType.Length)));
                }
            }
        }
    }
    return validator;
}

Thanks to IClassMetadata class exposed from Nhibernate, we can inspect the content of mapping browsing nhibernate object model in memory. In this situation I iterate for each mapped class, then for each property I check if it is a String property and look for maximum length. If it is different from zero it means that there is some maximum length specified in the mapping, so I create a corresponding rule. This simple code makes this test pass.

Core.Validator validator = ValidatorFromMetadata.GetValidatorFromSession(Factory);
EntityBase eb = new EntityBase() {MyName = new string('X', 51)};
ValidationResult res = validator.ValidateObject(eb);
Assert.That(res.Success, Is.False);
Assert.That(res.ErrorMessages[0], Is.EqualTo("Property MyName have a maximum length of 50"));

This verifies that the validator reflects rules contained in nhibernate mappings.

alk.

Tags:

Manage Scope or Execution Context of Repository

In a project I’m working with Guardian, we are using NHibernate behind a Repository Pattern. Despite the question if is good or not good to shield the session behind a Repository,we encountered a classic problem.

The software is structured as service, we use Castle Nhibernate Facility and Wcf Integration Facility plus a simple interceptor that manages the concept of “Single session per service call”. Now we are developing some interface in WPF, and this program can dialogate directly with database, without the need to access the db through wcf service. Since we are using MVVM, we makes heavy use of binding, and we like to use lazy load, to keep the logic simple. Instead of returning Dto, a special service class is used to directly return NHibernate persistent object, and if the user want to browse some internal collection of this object we can simply bind the view to entity collection property, and the collection will be fetched when needed with lazy load. Since we have a tree structure this solution is really simple and works really well.

The problem arise because the repository use a single session per call, so when the ViewModel ask for an object, the service return a disconnected object.

image

This happens because the repository does not have control over the lifetime of the session, it simply open the session, does whatever he need to does with the session, and then Dispose it. Nhibernate Castle integration helps a lot because it keeps track of session reference counting, so if you open a session, and in the same CallContext you open another session, you get a sort of a “weak reference” to the original session. This means that the session gets disposed only when the first created session is disposed.

To use Lazy load in the ViewModel we could simply call ISessionManager.OpenSession() in the constructor of the ViewModel and dispose resulting session in ViewModel Dispose() function. This is not good because it violates repository encapsulation, because you must know how repository is implemented internally.

Moreover it is really ugly to see a nhibernate ISession created in the ViewModel, only to keep session alive, because it vanished all benefit of shielding the ISession behind a repository. A better solution is to create a class like this one.

public class RepositoryScope : IDisposable
{
    private ISessionManager SessionManager { get; set; }
    private ISession Session { get; set; }

    protected RepositoryScope(ISessionManager manager)
    {
        SessionManager = manager;
    }

    #region IDisposable Members

    public void Dispose()
    {
        ///close the most external scope
        Session.Dispose();
    }

    #endregion

    public static RepositoryScope BeginScope()
    {
        RepositoryScope scope = new RepositoryScope(
            IoC.Resolve<ISessionManager>());
        scope.Session = scope.SessionManager.OpenSession(
            ConfigurationRegistry.MainDatabaseConnectionName);
        return scope;
    }
}

This simple class does internally a simple task, it creates a session and dispose in Dispose() method. In this way if you call RepositoryScope.BeginScope() you are actually creating a ISEssion that will be disposed when you will call Dispose() on the RepositoryScope object.

With this simple class the repository remains unchanged, and the caller can manage the lifecycle of repository context, without the need to know his internal implementation.

Alk.

Tags:

Manage In memory nhibernate test with sqlite and database schema

When you work with nhibernate you usually write a lot of tests that interact with your database, mainly to test your mapping but also when you do not want to shield the session behind a IRepository. To avoid Slow Test you should use some In Memory database like Sqlite, but it can be problematic when you use features of your real database (like Sql Server) that are not supported by the Sqlite engine.

One of the most frustrating one is the lack of schema support. When you have hundreds of entities, it is of fundamental importance that you use schema, avoiding to pollute the dbo with all the table, but this makes difficult to use Sqlite for testing purpose. Suppose you map an entity to the table myschema.RawMetabaseData, when you create the schema with schemaexport you will obtain this error

Error: Error during fixture setup NHibernate.HibernateException: SQLite error
unknown database myschema ---> System.Data.SQLite.SQLiteException: SQLite error
unknown database myschema

SQlite does not know the concept of schema, so it believes that myschema is a database name and not  a schema. There are some solution to this problem, but the most simple one is to change the mapping directly in memory.

cfg.AddAssembly("myassembly");
foreach (PersistentClass pc in cfg.ClassMappings)
{
    if (pc.Table.Name.Contains("."))
    {
        //this is a table with schema
        pc.Table.Name = pc.Table.Name.Replace(".", "_");
    }
}
ISessionFactory sf = cfg.BuildSessionFactory();

After you have called the AddAssembly() method, but before you create the SessionFactory with BuildSessionFactory(), mappings are compiled in memory, so you can browse the object model created from the xml mapping files, looking for table that contains dot character, and change the dot into an underscore, so the table can be used by Sqlite. This code is executed only for tests that uses SqlLite so my standard test that use Sql Server are not affected by the modification, but I can still use Sqlite to do fast in memory test even with tables in custom schema.

alk.

Tags: