Tag Archives: NHibernate

ConfORM: understand core pieces

6 Set

If you read the post ConfORM Gettins started you already know that NHibernate mapping task could be very easy with ConfORM. But unfortunately real project are a little bit more complicated that the example I shown in my first post about ConfORM so we need to understand how we can squeeze the tool to get the work done also when things get complicated.

The key to success is to understand which are the many configuration points of ConfORM: IDomainInspector and Mapper. This two pieces can be described as the head and the tail of the mapping process.
IDomainInspector is the entry point and also the mapping driver. This object is used to describe our domain model and currently there is only one concrete implementation of it: ObjectRelationalMapper. ObjectRelationalMapper class use ORM terminology to descride domain: TablePerClass, TablePerConcreteClass, ManyToMany, ManyToOne, OneToOne, Set, Bag, List, Poid, Component are just some of the methods you’ll find in it (they are also grouped inside the IObjectRelationalMapper interface but this is an implementation details). Another important thing to consider is that IDomainInspector is just an interface and this means that if don’t like the current implementation (ObjectRelationalMapper) you can write your own and use with ConfORM. For example you could create a DomainDrivenDesignMappper that has method like RootAggregate, ValueObjec an so on.
The other important object is the the Mapper.

namespace ConfOrm.NH
{
    public class Mapper
    {
        public Mapper(IDomainInspector domainInspector);
        public Mapper(IDomainInspector domainInspector, ICustomizersHolder customizerHolder);
        public Mapper(IDomainInspector domainInspector, IPatternsAppliersHolder patternsAppliers);

        public Mapper(IDomainInspector domainInspector, ICustomizersHolder customizerHolder,
                      IPatternsAppliersHolder patternsAppliers, ICandidatePersistentMembersProvider membersProvider);

        public IPatternsAppliersHolder PatternsAppliers { get; }

        public void Class<TRootEntity>(Action<IClassMapper<TRootEntity>> customizeAction) where TRootEntity : class;
        public void Subclass<TEntity>(Action<ISubclassMapper<TEntity>> customizeAction) where TEntity : class;

        public void JoinedSubclass<TEntity>(Action<IJoinedSubclassMapper<TEntity>> customizeAction)
            where TEntity : class;

        public void UnionSubclass<TEntity>(Action<IUnionSubclassMapper<TEntity>> customizeAction) where TEntity : class;
        public void Component<TComponent>(Action<IComponentMapper<TComponent>> customizeAction) where TComponent : class;

        public void Customize<TPersistent>(Action<IPersistentClassCustomizer<TPersistent>> customizeAction)
            where TPersistent : class;

        public void AddPoidPattern(Predicate<MemberInfo> matcher, Action<MemberInfo, IIdMapper> applier);
        public void AddPropertyPattern(Predicate<MemberInfo> matcher, Action<IPropertyMapper> applier);
        public void AddPropertyPattern(Predicate<MemberInfo> matcher, Action<MemberInfo, IPropertyMapper> applier);
        public void AddCollectionPattern(Predicate<MemberInfo> matcher, Action<ICollectionPropertiesMapper> applier);

        public void AddCollectionPattern(Predicate<MemberInfo> matcher,
                                         Action<MemberInfo, ICollectionPropertiesMapper> applier);

        public void AddManyToOnePattern(Predicate<MemberInfo> matcher, Action<IManyToOneMapper> applier);
        public void AddManyToOnePattern(Predicate<MemberInfo> matcher, Action<MemberInfo, IManyToOneMapper> applier);
        public void AddRootClassPattern(Predicate<Type> matcher, Action<Type, IClassAttributesMapper> applier);
        public void AddSubclassPattern(Predicate<Type> matcher, Action<Type, ISubclassAttributesMapper> applier);

        public void AddJoinedSubclassPattern(Predicate<Type> matcher,
                                             Action<Type, IJoinedSubclassAttributesMapper> applier);

        public void AddUnionSubclassPattern(Predicate<Type> matcher,
                                            Action<Type, IUnionSubclassAttributesMapper> applier);

        public void AddRootClassPattern(Predicate<Type> matcher, Action<IClassAttributesMapper> applier);
        public void AddSubclassPattern(Predicate<Type> matcher, Action<ISubclassAttributesMapper> applier);
        public void AddJoinedSubclassPattern(Predicate<Type> matcher, Action<IJoinedSubclassAttributesMapper> applier);
        public void AddUnionSubclassPattern(Predicate<Type> matcher, Action<IUnionSubclassAttributesMapper> applier);
        public HbmMapping CompileMappingFor(IEnumerable<Type> types);

        protected virtual Mapper.ICollectionElementRelationMapper DetermineCollectionElementRelationType(
            MemberInfo property, PropertyPath propertyPath, Type collectionElementType);

        public IEnumerable<HbmMapping> CompileMappingForEach(IEnumerable<Type> types);
        public void TypeDef<TComplex, TUserType>() where TUserType : IUserType;

        #region Nested type: ICollectionElementRelationMapper

        protected interface ICollectionElementRelationMapper
        {
            void Map(ICollectionElementRelation relation);
            void MapCollectionProperties(ICollectionPropertiesMapper mapped);
        }

        #endregion

        #region Nested type: IMapKeyRelationMapper

        protected interface IMapKeyRelationMapper
        {
            void Map(IMapKeyRelation relation);
        }

        #endregion
    }
}

If you look carefully at the namespace of this class you’ll notice the is “ConfOrm.NH” and if you analize the contructors you’ll understand that the ConfOrm.NH.Mapper class takes always an IDomainInspector and than analize and compile it to a HbmMapping instance via CompileMappingFor or CompileMappingForEach methods (read this post if you need a real sample of this usage)

With the methods on Mapper you can customize its behavior. For example you can customize the table name mapped to a class with this line of code:

mapper.Class<Answer>(cm => cm.Table("a_dummy_table_name"));

or set the Length of the properties using this expression:

mapper.Customize<Name>(name =>
{
             name.Property(np => np.First, pm => pm.Length(20));
             name.Property(np => np.Last, pm => pm.Length(35));
});

This two tasks (table names and property length) aren’t detail of the domain model. They are questions related to the database that’s the reason why the are handled by the Mapper class and not by the IDomainInspector.

Last but not least in the future someone could be interested in using ConfORM with another ORM. All you need to do is to write your own Mapper.

Are you ConfORM ?

Annunci

ConfORM – Output XML mappings

5 Set

As described in this post ConfORM doesn’t desirialize xml files into C# objects as many others tools do therefore no xml is produced by default by ConfORM. But many times, for NHibernate users, watching an hbm.xml files is just the simplest way to detect how NHibernate is configured. This the reason why the leatest bits of ConfORM contains a class callled NHibernateMappingsExtensions (inside ConfOrm.UsageExamples project) that is just a util extensions to use in your tests or where you need to see the XML mappings. This class doesn’t do anything special: it serializes instances of HbmMapping objects, that is the NHibernate‘s object holding the mapping information, into xml. The HbmMapping instance is created by the ConfORM Mapper class which a class that any ConfORM‘s users should know very well since it’s one the key pieces of the tool (I’ll blog about it soon).

NHibernateMappingsExtensions usage it very simple. Copy & paste this class inside your test project and write down a test that set up ConfORM correctly

and finally call the method AsString() if you want to see all the xml in the output console like this

or call the method WriteAllXmlMapping() if you want a single .hbm.xml file for each domain object on the disk

You could find more samples of this two methods in the ConfOrm.UsageExamples project.

ConfORM- Getting started

2 Set

ConfORM is a just another option to create the mapping for NHibernate. Out-of-the-box NHibernate mapping’s can be created only with XML mapping files (*.hbb.xml files). ConfORM uses code instead of XML. If you are thinking that there is already at least one tool that follow this approach you should read this post to understand which are the differences between the other tools and ConfORM.
After this brief introduction I want to show you a little example of ConfORM’s usage to demonstrate how easy could be life with this tool.  
Here below I posted a screenshot of my domain model. As you can see there is nothing special here: some Collections and some ManyToOne relationships. There is also a read-only property: Element.HasGroupedAnswer (sorry you can’t see it in the screenshot below) .

image

The very first step is to create a valid configuration object for NHibernate. Using NHibernate.Cfg.Loquacious feature from NHibernate version 3 this can be done with this lines of code.

            Configuration configure = new Configuration();
            configure.SessionFactoryName("Demo");
            configure.Proxy(p =>
            {
                p.Validation = false;
                p.ProxyFactoryFactory<ProxyFactoryFactory>();
            });
            configure.Properties["current_session_context_class"] = _context;
            configure.DataBaseIntegration(db =>
            {
                db.Dialect<MsSql2008Dialect>();
                db.Driver<SqlClientDriver>();
                db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
                db.IsolationLevel = IsolationLevel.ReadCommitted;
                db.LogFormatedSql = LogSqlInConsole;
                db.LogSqlInConsole = LogSqlInConsole;                
                db.ConnectionString = "yuor connection string goes here";
                db.Timeout = 10;
                db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
            });
 

Once configuration object is ready we must setup mapping for our domain. Now it’s time to use ConfORM. I decided to wrap all mapping stuff inside the class shown below.

    public class ConfOrmDomainMapper
    {
        public ConfOrmDomainMapper()
        {
            orm = new ObjectRelationalMapper();         
            orm.Patterns.PoidStrategies.Add(new NativePoidPattern());
            var patternsAppliers = new CoolPatternsAppliersHolder(orm);

            mapper = new Mapper(orm, patternsAppliers);

            DefineDomain();
            DefineMapping();
        }

        private void DefineMapping()
        {           
        }

        private void DefineDomain()
        {
            orm.TablePerClass(GetTypes());
        }

        public HbmMapping Mapping
        {
            get { return mapper.CompileMappingFor(GetTypes()); }
        }

        IEnumerable<Type> GetTypes()
        {
            var type = typeof(BaseEntity);
            return type.Assembly.GetTypes().Where(t => t.BaseType == type);
        }
        private readonly ObjectRelationalMapper orm;
        private readonly Mapper mapper;
    }

Since all the classes in my domain model inherit from a common class called BaseEntity I can use reflection to get all types with a single line of code. This is not mandatory for ConfORM but as you can the result is very nice. In this simple sample I don’t need to customize the mapper but if you need to do you can use the Mapper class instance. Now all the mapping is already done. Since I used CoolPatternsAppliersHolder ConfORM already create a complete mapping to start working with NHibernate (I’ll blog some details about it in the future)

To finish I need to join ConfORM mappings and NHibernate configuration. This task is done with this few lines of code:

ConfOrmDomainMapper domainMapper = new ConfOrmDomainMapper();
configure.AddDeserializedMapping(domainMapper.Mapping, "MyAppDomain");

After this I’m ready to create a SessionFactory with

configure.BuildSessionFactory();

and start using NHibernate a usual.

This was just the beginning. I hope to find some more time to write again about ConfORM.

I want also thank Fabio Maulo for sharing with community this another piece of great code!

NHibernate 2.1.0 beta 2

21 Giu

Today NHibernate team released version 2.1.0 beta 2

This version contains a fix for bug that cause some problem with MS Sql server execution plans (more details here).

Be carefull if you use an HQL query cause the actual parser is not still able to use the correct parameter type. I hope also this issue will be solved in the future versions but in the meantime you can use the solution I wrote in post linked above.

Technorati Tag: ,

LINQ to NHibernate

19 Apr

This is a very great news for NHibernate!

Time for another update; it’s been a while since the last one. Good progress has been made on the new HQL AST Parser port from Hibernate, and it’s now in a state where it’s usable, passing the vast majority of the tests within the NHibernate test suite. Of those that are failing (around 8 out of over 1600), we are currently discussing on the developers group whether we will live with these minor breaking changes, or do continued work on the parser to get everything passing.

NHibernate queries & sql server execution plans

10 Mar

In these days I’m working with Andrea on a web application using ASP.NET & SQL Server . You should kwon that Andrea, is a SQL Server MVP, but fortunately is not a T-SQL taliban so he agree without hesitation when I proposed to use NHibernate to build our data access layer. When a tool can help us while building an application why not use it ?

As you can image a skilled DBA like Andrea can rapidly find a bottleneck in any query fired to a database. While writing a simple query using NHibernate API we noticed a strange behavior of NHibernate 2.0 with parameters. Here is the code we wrote:

using (ISession session = factory.OpenSession())
{
	//nvarchar(4)
	session.CreateQuery("from Region r where r.Description like :desc")
		.SetString("desc", "zon%")
		.List();

	//nvarchar(5)
	session.CreateQuery("from Region r where r.Description like :desc")
		.SetString("desc", "zone%")
		.List();
}

This code generates these two sql queries:

exec sp_executesql N'select region0_.RegionId as RegionId0_,
region0_.RegionDescription as RegionDe2_0_
from Region region0_ where
(region0_.RegionDescription like @p0 )
',N'@p0 nvarchar(4)',@p0=N'zon%' exec sp_executesql N'select region0_.RegionId as RegionId0_,
region0_.RegionDescription as RegionDe2_0_
from Region region0_ where
(region0_.RegionDescription like @p0 )
',N'@p0 nvarchar(5)',@p0=N'zone%'

As you can see the parameter type is different in the two statements. This unfortunately cause SqlServer to generate two different exection plans. If you need the best performace you should avoid this behaviour. If you send to the database queries using the same parameter (type & size) SqlServer use his internal cache. You have already pay for it so why not to use it ?

I had the pleasure to discuss with Fabio Maulo about this topic (you can read here. The conversation is in italian). At the end I found this solution:

I edited my hibernate.cfg.xml file adding this node to the xml

<property name="prepare_sql">true</property>

and then I wrote this code:

using (ISession session = factory.OpenSession())
{

	//nvarchar(10)
	session.CreateQuery("from Region r where r.Description like :desc")
		.SetParameter("desc", "zoneh%", TypeFactory.GetStringType(10))
		.List();

	//nvarchar(10)
	session.CreateQuery("from Region r where r.Description like :desc")
		.SetParameter("desc", "neh%", TypeFactory.GetStringType(10))
		.List();
}

Now with this code NHibernate generates these two sql queries:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@p0 nvarchar(10)',N'select region0_.RegionId as RegionId0_, 
region0_.RegionDescription as RegionDe2_0_ from Region region0_ where
(region0_.RegionDescription like @p0 )
',@p0=N'zoneh%' select @p1 declare @p1 int set @p1=2 exec sp_prepexec @p1 output,N'@p0 nvarchar(10)',N'select region0_.RegionId as RegionId0_,
region0_.RegionDescription as RegionDe2_0_ from Region region0_ where
(region0_.RegionDescription like @p0 )
',@p0=N'neh%' select @p1

This is better cause the parameters length is always the same. Now your database will thank you for this! But wait you can still do something better.

If your database column type is a varchar and not nvarchar you can improve your code using a different SqlDbType. How ? Whit his code:

using (ISession session = factory.OpenSession())
{
	session.CreateQuery("from Region r where r.Description like :desc")
		.SetParameter("desc", "z%", TypeFactory.GetAnsiStringType(15))
		.List();

	session.CreateQuery("from Region r where r.Description like :desc")
		.SetParameter("desc", "za%", TypeFactory.GetAnsiStringType(15))
		.List();
}

Now the parameter type is a varchar and the size is always 15. Whit this latest improvement you remove a CONVER_IMPLICIT operation from your query execution plan (futher details here but in italian ). This cast operation cost something so if you don’t need it why to remove it ?

I hope this will help someone (dev & dba) in the future!