NHibernate 3 and Autofac, can go together? (part 1)

In this post, I intend to explain about NHibernate and integrated it with Autofac. And some content that I will write as:

  • NHibernate
  1. Introduce about NHibernate 3
  2. Fluent NHibernate and its conventions
  3. Relationship Mapping (1-1, 1-*, *-*), value object
  4. Domain first, build entity, mapping classes, auto generated database schema
  • Autofac
  1. Introduce about Autofac
  2. Inversion of Control (IoC)
  3. AOP (Interceptor)
  4. Module (separated of concern: SoC)
  • Intergrated NHibernate into Autofac
  1. Register essential components of NHibernate
  2. Register services (Repository, Interceptor)
  3. Intergrated CommonServiceLocator into Autofac
  4. Wrap CommonServiceLocator with IoC wrapper (Demeter law)
  5. Boot your application

First of all, you can got all source code at my github 

Part 1:  NHibernate

  • Introduce about NHibernate 3 Alpha 2

NHibernate is a .NET port of Java’s Hibernate. It is designed to bring interactions with RDBMS solutions in application code in line with the current object-oriented application design. Instead of spending time writing SQL and code to map the data stored in your database into your application objects, NHibernate takes this off your hands. It allows you to spend more time writing the domain code, and providing a more robust and reliable solution.

Beside, NHibernate also have more intend from community. Go to NHibernate Forge at here and here for more information about it.

NHibernate 3 going to ship with number of new features like:

          + Lazy load columns: Ayende have great article about it at here

          + Allow DetachedCriteria To Work with IStatelessSession:

IStatelessSession it’s the interface that allow us to use sessions without first-level cache. Specially created for do bulks Saves/Updates/Deletes. IStateless has a similar semantic to ISession. Now, ISessionFactory it’s responsible of the factory of ISession as well too, IStatelessSession.

DetachedCriteria’s brother but to HQL, this feature allow us to create queries detached of a Session, that help to play at our DAO implementation. It was a miss feature at NHibernate, to many things you cannot do with Criteria. This implementation was available to many times ago at uNHAddIns, you can see here and here.

          + Add ability to delimit aliases in generated SQL

          + QueryOver equality to null should generate (x is null or x == value) (cool feature that I will intend to write about it)

The NHibernate Profiler, a separate commercial project maintained by Hibernating Rhinos,  is a  debugging tool for analyzing the usage of NHibernate in projects, especially the SQL generated. The tool is expected to be updated to support NHibernate 3.

HQL Language Service for Visual Studio is a new Visual Studio Addin by José F. Romaniello, offering Hibernate Query Language(HQL) syntax highlighting and checking, and partial Intellisense support.

And now, I also have known one project that will make the NHibernate cooler, named ConfORM. You can read this for know more about it.
  • Fluent NHibernate and its conventions
What about Fluent NHibernate? Just make Fluent, XML-less, compile safe, automated,convention-based mappings for NHibernate. But how do you do when doesn’t NHibernate support you? That is the reason the Convention come to. For example, you want to customize the table name, name of foreign key, name of entities, override something in primary key, foreign key,…I have some convention available for someone that want to use it at below:
    public class PrimaryKeyNameConvention : IIdConvention
    {
        public void Apply(IIdentityInstance instance)
        {
            instance.Column(instance.EntityType.Name + "Id");
        }
    } 
    public class ForeignKeyNameConvention : IHasManyConvention
    {
        public void Apply(IOneToManyCollectionInstance instance)
        {
            instance.Key.Column(instance.EntityType.Name + "Id");
        }
    }

    public class ReferenceConvention : IReferenceConvention     {         public void Apply(IManyToOneInstance instance)         {             instance.Column(instance.Property.Name + "Id");         }     }
    public class TableNameConvention : IClassConventionIClassConventionAcceptance
    {
        public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
        {
            criteria.Expect(x => x.TableNameIs.Not.Set);
        }
        public void Apply(IClassInstance instance)
        {
            instance.Table(instance.EntityType.Name + "s");
        }
    }
If you need something else, you can continue to customize it, same to you!
  • Domain first, build entity, mapping classes, relationship Mapping (1-1, 1-*, *-*), value object, auto generated database schema

This is part that made me spend much time to it. Because it really important if you want to mapping with bi-directional. For you to easy imagine, I will give a sort example about it. I assume that we have a News Management System (just simple!). One Category will have many News, and certainly one News just belong to one Category. And one News will have one Poll for it and else one Poll also have one News. But one Poll will be voted by many Users, and one User can vote many Poll. And below is my work on it:

Today, domain first is not strange to anyone, but someone also have a little knowledge about it. So what is domain first? Whenever you use NHibernate you will be realize that Domain is first thing you should do when you map the entity with your database schema, and the second work is make mapping class, and finally tell the NHibernate auto generating the database schema for you. Why do we do this? Because your RDBMS is relationship database and have different structure with your model, so we need some steps to transform it between RDBMS and Application Domain. Just normally! Don’t care about it. Another solution is using the Object Oriented Database, so you don’t need to make transformation, just get all your entities and save to OODB. In this post only focus on RDBMS.

+ Domain entities:

    public class EntityBase : IEntity
    {
        public virtual int Id { getset; }
    }

 

    public class Category : EntityBaseICategory
    {
        public virtual string Name { getset; }
        public virtual DateTime CreatedDate { getset; }
        public virtual string Description { getset; }
        public virtual IList<INewsNews { getset; }
        public Category()
        {
            News = new List<INews>();
        }
        public virtual void AssignNews(INews news)
        {
            news.Category = this;
            News.Add(news);
        }
    }
    public class News : EntityBaseINews
    {
        public virtual string Title { getset; }
        public virtual string ShortDescription { getset; }
        public virtual string Content { getset; }
        public virtual ICategory Category { getset; }
        public virtual IPoll Poll { getset; }
        public virtual void AssignPoll(IPoll poll)
        {
            Poll = poll;
        }
        public virtual void AssignCategory(ICategory cat)
        {
            cat.News.Add(this);
            Category = cat;
        }
    }

    public class Poll : EntityBaseIPoll     {          public virtual int Value { getset; }         public virtual DateTime VoteDate { getset; }         public virtual string WhoVote { getset; }         public virtual INews News { getset; }         public virtual ISet<IUserUsers { getset; }         public Poll()         {             Users = new Iesi.Collections.Generic.HashedSet<IUser>();         }         public virtual void AssignNews(INews news)         {             News = news;         }         public virtual void AssignUser(IUser user)         {             user.Polls.Add(this);             Users.Add(user);         }
    }

    public class User : EntityBaseIUser     {         public virtual string UserName { getset; }         public virtual string Password { getset; }         public virtual Address Address { getset; }         public virtual ISet<IPollPolls { getset; }         public User()         {             Polls = new Iesi.Collections.Generic.HashedSet<IPoll>();         }         public virtual void AssignPoll(IPoll poll)         {             poll.Users.Add(this);             Polls.Add(poll);         }     }

 Value object, immutable object:
    public class Address
    {
        public Address()
            : this("""""")
        {
        }
        public Address(string street, string ward, string district)
        {
            Street = street;
            Ward = ward;
            District = district;
        }
        public virtual string Street { getprivate set; }
        public virtual string Ward { getprivate set; }
        public virtual string District { getprivate set; }
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Address)) return false;
            return Equals((Address) obj);
        }
        public bool Equals(Address other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.StreetStreet) && Equals(other.WardWard) && Equals(other.DistrictDistrict);
        }
        public override int GetHashCode()
        {
            unchecked
            {
                int result = (Street != null ? Street.GetHashCode() : 0);
                result = (result*397) ^ (Ward != null ? Ward.GetHashCode() : 0);
                result = (result*397) ^ (District != null ? District.GetHashCode() : 0);
                return result;
            }
        }
    } 

+ One to many (Category<->News, News<->Category) mapping class

    public class CategoryMapping : ClassMap<Category>
    {
        public CategoryMapping()
        {
            
            HasMany<News>(x => x.News)
                .Key(key => key.Nullable())
                .Inverse()
                .LazyLoad()
                .Cascade.AllDeleteOrphan();
        }
    }
    public class NewsMapping : ClassMap<News>
    {
        public NewsMapping()
        {
            References(x => (Category)x.Category)
                .Nullable()
                .Cascade.None();
        }
    }

+ One to one (News<->Poll, Poll<->News) mapping class

    public class NewsMapping : ClassMap<News>
    {
        public NewsMapping()
        {
            HasOne<Poll>(x => x.Poll)
                .Constrained()
                .PropertyRef(x => x.News)
                .Cascade.Delete();
        }
    }
    public class PollMapping : ClassMap<Poll>
    {
        public PollMapping()
        {
            
            References<News>(x => x.News)
                .Column("NewsId")
                .Unique()
                .Cascade.None();
        }
    }

+ Many to many (Poll<->User, User<->Poll) mapping class

    public class PollMapping : ClassMap<Poll>
    {
        public PollMapping()
        {
            
            HasManyToMany<User>(x => x.Users)
                .Table("PollUsers")
                .ParentKeyColumn("PollId").Cascade.SaveUpdate()
                .ChildKeyColumn("UserId").Cascade.None()
                .Generic()
                .Cascade.SaveUpdate()
                .AsSet();
        }
    }

 

    public class UserMapping : ClassMap<User>
    {
        public UserMapping()
        {
            HasManyToMany<Poll>(x => x.Polls)
                .Table("PollUsers")
                .ParentKeyColumn("UserId").Cascade.SaveUpdate()
                .ChildKeyColumn("PollId").Cascade.None()
                .Generic()
                .Inverse()
                .LazyLoad()
                .AsSet();
        }
    }

 + Mapping for value object:

    public class UserMapping : ClassMap<User>
    {
        public UserMapping()
        {
            Component(x => x.Address, m =>
            {
                m.Map(x => x.Street"Street").Length(30);
                m.Map(x => x.Ward"Ward").Length(20);
                m.Map(x => x.District"District").Length(20);
            });            
        }
    }

 That's it. And I want to finish part 1 at here, don't need to talk anymore. Happy coding and see you next time!

Shout it

6 Comments

Comments have been disabled for this content.