The same domain with ConfORM and NHibernate

In this post I will map the same domain I used in the previous post about EntityFramework but without the overhead of the crazy properties I had to add to fix the deficiencies of EntityFramework 4.1:

2011-03-29_2015_001

For this post I’m using the following nugets:

  • ConfORM (depends on NHibernate): This help me to automap the domain to a relational model based on conventions.
  • NHibernate.LinFu: This is a proxy generator used by nhibernate for lazy loading. Note that the next version of NHibernate 3.2 (trunk) doesn’t need this. It is inside NHibernate.dll
  • NHibernate.SetForNet4: This is because NHibernate doesn’t support System.Collections.Generic.ISet<T> out of the box. ISet<T> is from .Net 4, while nhibernate support set mapping from version 1 with Iesi.Collections. This nuget add support for lazy loading an ISet<T>

This is a console project and I have the following files:

2011-03-31_1047

Net4Collections is a class installed by the NHibernate.SetForNet4, so the only I did for this example was:

internal class Program
{
    private static void Main()
    {
        HbmMapping mappings = AutomapDomain(typeof (Order),
                                            typeof (OrderItem),
                                            typeof (Product),
                                            typeof (Customization));

        Configuration configuration = ConfigureNHibernate(mappings);
        Console.WriteLine("Generating the schema");
        new SchemaExport(configuration).Create(true, true);

        Console.WriteLine("Persiting some objects");
        var sf = configuration.BuildSessionFactory();
        
        using(var s = sf.OpenSession())
        using(var tx = s.BeginTransaction())
        {
            s.Save(new Product
                       {
                           Name = "Fideos",
                           Customizations = {new Customization
                                                 {
                                                     Name = "Tuco",
                                                     PossibleValues = {"Pocon", "Medio", "Sopa"}
                                                 }}
                       });
            tx.Commit();
        }
        Console.ReadLine();
    }

    private static HbmMapping AutomapDomain(params Type[] entities)
    {
        var orm = new ObjectRelationalMapper();
        //We are telling here to conform that ISet<> properties should be mapped as <set>
        orm.Patterns.Sets.Add(
            m => m.GetPropertyOrFieldType()
                  .GetGenericIntercafesTypeDefinitions()
                  .Contains(typeof (ISet<>)));

        orm.TablePerClass(entities);
        CustomizeMappings(orm);

        //Some ConfORM.Shop patterns
        var englishInflector = new EnglishInflector();
        var patterns = new SafePropertyAccessorPack()
            .Merge(new CoolTablesAndColumnsNamingPack(orm))
            .Merge(new PluralizedTablesPack(orm, englishInflector)
            .Merge(new CollectionOfElementsColumnApplier(orm, englishInflector)));

        //Mapper
        var mapper = new Mapper(orm, patterns);

        var mapping = mapper.CompileMappingFor(entities);
        Console.WriteLine(Serialize(mapping));
        return mapping;

    }

    private static void CustomizeMappings(ObjectRelationalMapper orm)
    {
        orm.PersistentProperty<Order>(o => o.Total);
        orm.Cascade<Product, Customization>(Cascade.All);
    }

    private static Configuration ConfigureNHibernate(HbmMapping mapping)
    {
        var configuration = new Configuration();

        //nhibernate support for ISet<>
        configuration.CollectionTypeFactory<Net4CollectionTypeFactory>();

        //Use linfu proxy facotory: NOTE: This is going to be deprecated in vNext.
        configuration.Proxy(p => p.ProxyFactoryFactory<ProxyFactoryFactory>());

        //The database connection configuration
        configuration.DataBaseIntegration(db =>
                                              {
                                                  db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
                                                  db.Dialect<MsSql2008Dialect>();
                                                  db.Driver<SqlClientDriver>();
                                                  db.ConnectionStringName = "NHibernateTest";
                                                  db.LogSqlInConsole = true;
                                                  db.LogFormatedSql = true;
                                              });


        configuration.AddDeserializedMapping(mapping, "AllMappings");

        return configuration;
    }

    //This method is only used to show you in the console the nhibernate mappings in XML 
    protected static string Serialize(HbmMapping hbmElement)
    {
        var setting = new XmlWriterSettings {Indent = true};
        var serializer = new XmlSerializer(typeof (HbmMapping));
        using (var memStream = new MemoryStream())
        using (XmlWriter xmlWriter = XmlWriter.Create(memStream, setting))
        {
            serializer.Serialize(xmlWriter, hbmElement);
            memStream.Flush();
            memStream.Position = 0;

            var sr = new StreamReader(memStream);
            return sr.ReadToEnd();
        }
    }
}

There are only one customization in this example to map the readonly property. By default ConfORM doesn’t map readonly properties but as I said yesterday I was trying to do so in EntityFramework…
I am using a set of conventions to map the domain, the power of ConfORM to infer most of the things and also ConfORM.Shop.dll that comes with the ConfORM nuget package to generate this beautiful schema:

2011-03-31_1115

Please take your time to examine this schema.

Want to see the output of the console?

Generating the schema

    if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3E8588581FEFE3AB]') AND parent_object_id = OBJECT_ID('OrderItems'))
alter table OrderItems  drop constraint FK3E8588581FEFE3AB


    if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3E858858F373BEAE]') AND parent_object_id = OBJECT_ID('OrderItems'))
alter table OrderItems  drop constraint FK3E858858F373BEAE


    if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FKEF9298D4286FF938]') AND parent_object_id = OBJECT_ID('OrderItemPreferences'))
alter table OrderItemPreferences  drop constraint FKEF9298D4286FF938


    if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FKC2F496971FEFE3AB]') AND parent_object_id = OBJECT_ID('Customizations'))
alter table Customizations  drop constraint FKC2F496971FEFE3AB


    if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK956AE80DAF824711]') AND parent_object_id = OBJECT_ID('CustomizationPossibleValues'))
alter table CustomizationPossibleValues  drop constraint FK956AE80DAF824711


    if exists (select * from dbo.sysobjects where id = object_id(N'Orders') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Orders

    if exists (select * from dbo.sysobjects where id = object_id(N'OrderItems') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table OrderItems

    if exists (select * from dbo.sysobjects where id = object_id(N'OrderItemPreferences') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table OrderItemPreferences

    if exists (select * from dbo.sysobjects where id = object_id(N'Products') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Products

    if exists (select * from dbo.sysobjects where id = object_id(N'Customizations') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customizations

    if exists (select * from dbo.sysobjects where id = object_id(N'CustomizationPossibleValues') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table CustomizationPossibleValues

    if exists (select * from dbo.sysobjects where id = object_id(N'hibernate_unique_key') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table hibernate_unique_key

    create table Orders (
        Id BIGINT not null,
       [Date] DATETIME null,
       Status INT null,
       Location INT null,
       primary key (Id)
    )

    create table OrderItems (
        Id BIGINT not null,
       ProductId BIGINT null,
       Quantity INT null,
       UnitPrice DECIMAL(19,5) null,
       OrderId BIGINT null,
       primary key (Id)
    )

    create table OrderItemPreferences (
        OrderItemId BIGINT not null,
       Preference NVARCHAR(255) null,
       idx NVARCHAR(255) not null,
       primary key (OrderItemId, idx)
    )

    create table Products (
        Id BIGINT not null,
       Name NVARCHAR(255) null,
       Price DECIMAL(19,5) null,
       primary key (Id)
    )

    create table Customizations (
        Id BIGINT not null,
       Name NVARCHAR(255) null,
       ProductId BIGINT null,
       primary key (Id)
    )

    create table CustomizationPossibleValues (
        CustomizationId BIGINT not null,
       PossibleValue NVARCHAR(255) null
    )

    alter table OrderItems 
        add constraint FK3E8588581FEFE3AB 
        foreign key (ProductId) 
        references Products

    alter table OrderItems 
        add constraint FK3E858858F373BEAE 
        foreign key (OrderId) 
        references Orders

    alter table OrderItemPreferences 
        add constraint FKEF9298D4286FF938 
        foreign key (OrderItemId) 
        references OrderItems

    alter table Customizations 
        add constraint FKC2F496971FEFE3AB 
        foreign key (ProductId) 
        references Products

    alter table CustomizationPossibleValues 
        add constraint FK956AE80DAF824711 
        foreign key (CustomizationId) 
        references Customizations

    create table hibernate_unique_key (
         next_hi BIGINT 
    )

    insert into hibernate_unique_key values ( 1 )
Persiting some objects
NHibernate: 
    select
        next_hi 
    from
        hibernate_unique_key with (updlock,
        rowlock)
NHibernate: 
    update
        hibernate_unique_key 
    set
        next_hi = @p0 
    where
        next_hi = @p1;
    @p0 = 2 [Type: Int64 (0)], @p1 = 1 [Type: Int64 (0)]
NHibernate: 
    select
        next_hi 
    from
        hibernate_unique_key with (updlock,
        rowlock)
NHibernate: 
    update
        hibernate_unique_key 
    set
        next_hi = @p0 
    where
        next_hi = @p1;
    @p0 = 3 [Type: Int64 (0)], @p1 = 2 [Type: Int64 (0)]
NHibernate: 
    INSERT 
    INTO
        Products
        (Name, Price, Id) 
    VALUES
        (@p0, @p1, @p2);
    @p0 = 'Fideos' [Type: String (4000)], @p1 = 0 [Type: Decimal (0)], @p2 = 32768 [Type: Int64 (0)]
NHibernate: 
    INSERT 
    INTO
        Customizations
        (Name, Id) 
    VALUES
        (@p0, @p1);
    @p0 = 'Tuco' [Type: String (4000)], @p1 = 65536 [Type: Int64 (0)]
NHibernate: 
    UPDATE
        Customizations 
    SET
        ProductId = @p0 
    WHERE
        Id = @p1;
    @p0 = 32768 [Type: Int64 (0)], @p1 = 65536 [Type: Int64 (0)]
NHibernate: 
    INSERT 
    INTO
        CustomizationPossibleValues
        (CustomizationId, PossibleValue) 
    VALUES
        (@p0, @p1);
    @p0 = 65536 [Type: Int64 (0)], @p1 = 'Pocon' [Type: String (4000)]
NHibernate: 
    INSERT 
    INTO
        CustomizationPossibleValues
        (CustomizationId, PossibleValue) 
    VALUES
        (@p0, @p1);
    @p0 = 65536 [Type: Int64 (0)], @p1 = 'Medio' [Type: String (4000)]
NHibernate: 
    INSERT 
    INTO
        CustomizationPossibleValues
        (CustomizationId, PossibleValue) 
    VALUES
        (@p0, @p1);
    @p0 = 65536 [Type: Int64 (0)], @p1 = 'Sopa' [Type: String (4000)]

In this domain we have things like ISet and IDictionary (the OrderItemPreferences table).
It is a quite simple example but as you can see ConfORM resolve most of the things *automagicallly although you can customize everything.

There is also an inflector for Spanish and Italian in ConfORM.Shop. You don’t need an inflector to work with ConfORM you can remove all that part and use singular names.

I'll publish this example somewhere soon. But if you need to get into ConfORM I strongly recommend you to see the ConfOrm.UsageExamples and also NHibernate.Mystic.

I did a lot of corrections to this article after published; thank Fabio Maulo very much for doing a deep review.


blog comments powered by Disqus
  • Categories

  • Archives