Try fast search NHibernate

15 November 2010

ConfORM : Any-to-Many

In the previous post I have asked a help about well known implementation patterns using interfaces in relations. Even if the feedback was zero I’m happy to say that ConfORM is now supporting case-1-2-3-4. In the meaning I was thinking about the case-6.
An interface is implemented by more than one root-entity or no-root-entity, and thus by its own hierarchies, and it is used as a bidirection-one-to-many
To explain the case I’ll use a chunk of Ayende’s nhibernate-blog-model a little bit complicated for the show-case.
public interface ITagable
{
    string Title { get; }
    string Summary { get; }
}

public class Blog : Entity, ITagable
{
    public Blog()
    {
        Tags = new HashSet<Tag>();
    }
    public virtual string Title { get; set; }
    public virtual string Subtitle { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public virtual string Summary
    {
        get { return Subtitle; }
    }
}

public class Post : Entity, ITagable
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }
    public virtual string Title { get; set; }
    public virtual string Content { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public virtual string Summary
    {
        get { return Content != null ? Content.Substring(0, Content.Length < 300 ? Content.Length : 300) : Content; }
    }
}

public class Tag : Entity
{
    public virtual string Name { get; set; }
    public virtual ITagable TagedItem { get; set; }
}
Let me highlight some things:

  • ITagable is implemented by two root-entities (two different hierarchies).
  • In Blog and in Post the property Summary does not need to be persisted.
  • Blog and in Post has a collection of Tag.
  • Tag has a property of type ITagable where I can assign a Blog or a Post.
The mapping of this domain is a nice exercise and the code to see how will work your mapping is:
public static class Program
{
    private static void Main(string[] args)
    {
        var nhinit = new NHibernateInitializer();
        nhinit.Initialize();
        nhinit.CreateSchema();
        ISessionFactory sf = nhinit.SessionFactory;

        using (ISession s = sf.OpenSession())
        {
            using (ITransaction tx = s.BeginTransaction())
            {
                var blog1 = new Blog();
                blog1.Title = "NHibernate blog.";
                blog1.Subtitle = "Tricks and tips about NHibernate.";
                blog1.Tags.Add(new Tag {Name = "NHibernate", TagedItem = blog1});

                var post1 = new Post();
                post1.Title = "The difference between Get, Load and querying by id";
                post1.Content = "One of the more common mistakes ...";
                post1.Tags.Add(new Tag {Name = "NHibernate", TagedItem = post1});
                post1.Tags.Add(new Tag {Name = "querying", TagedItem = post1});

                s.Save(blog1);
                s.Save(post1);
                tx.Commit();
            }
        }

        using (ISession s = sf.OpenSession())
        {
            using (ITransaction tx = s.BeginTransaction())
            {
                Blog blog1 = (from b in s.Query<Blog>() select b).Single();
                Console.WriteLine("Blog: " + blog1.Title + " Tags:" + blog1.Tags.AsString());
                Post post1 = (from p in s.Query<Post>() select p).Single();
                Console.WriteLine("Post: " + post1.Title + " Tags:" + post1.Tags.AsString());
                tx.Commit();
            }
        }
        using (ISession s = sf.OpenSession())
        {
            using (ITransaction tx = s.BeginTransaction())
            {
                foreach (Tag tag in from t in s.Query<Tag>() select t)
                {
                    Console.WriteLine("Tag: " + tag.Name);
                    Console.WriteLine("  Item   :" + tag.TagedItem.Title);
                    Console.WriteLine("  Summary:" + tag.TagedItem.Summary);
                }
                tx.Commit();
            }
        }

        Console.WriteLine("Work done!");
        Console.ReadLine();
        sf.Close();
        nhinit.DropSchema();
    }

    private static string AsString(this IEnumerable<Tag> tags)
    {
        return string.Join(", ", tags.Select(x => x.Name).ToArray());
    }
}

You can implements the class NHibernateInitializer as you want, what is important is that you have to create the DB using NHibernate without touch it by hands and then, obviously, you have to see right results.

One dream one soul one prize one goal
The mapping using ConfORM is:
public HbmMapping GetMapping()
{
    var domainEntities = new[] {typeof (Blog), typeof (Post), typeof (Tag)};
    var orm = new ObjectRelationalMapper();
    var mapper = new Mapper(orm, new CoolPatternsAppliersHolder(orm));

    orm.Patterns.PoidStrategies.Add(new HighLowPoidPattern(new {max_lo = 100}));
    orm.Patterns.Sets.Add(new UseSetWhenGenericCollectionPattern());
    orm.TablePerClass(domainEntities);

    orm.Cascade<Blog, Tag>(Cascade.All);
    orm.Cascade<Post, Tag>(Cascade.All);

    mapper.Customize<Post>(cm => cm.Property(p => p.Content, pm => pm.Type(NHibernateUtil.StringClob)));

    return mapper.CompileMappingFor(domainEntities);
}


When something exceeds your ability to understand how it works, it becomes magical.
ConfORM a kind of magic.

05 November 2010

ConfORM : Thoughts about interfaces as relation

As said in the previous post the most “easy” part related to polymorphism is done. Now is the time to give more intelligence to the “muchachota” (some BuenosAires guy will understand what it mean… btw, for others, with  “muchachota” I’m referring to ConfORM project).

I need your help thinking about the behavior that ConfORM should have in these cases:

Case 1:
An interface is implemented only by a root-entity and thus by the whole hierarchy.

Case 2:
An interface is implemented only by an entity (not root-entity) and thus by its own hierarchy-branch.

Case 3:
An interface is implemented only by a component and thus by its own hierarchy.

Case 4:
An interface is implemented by more than one root-entity or no-root-entity and thus by its own hierarchies

Case 5:
An interface is implemented by a root-entity, a no-root-entity, a component and it is used in a relation (close the house and go to the church).

ConfORM should discover all cases:
The ObjectRelationalMapper (IDomainInspector) should provide the correct response of IsEntity, IsRootEntity, IsManyToOne, IsComponent, IsHeterogeneousAssociation.
The Mapper, perhaps through the IDomainInspector, should have an applier to be able to apply the correct concrete-class in each case.

For case-1 and case-2 ConfORM may generate even the node <import> to use those interfaces in HQL.
For the case-4 ConfORM should provide a way to interpreter the relation as heterogeneous-association by default.
For the case-5 I don't think I can do something.

NOTE: each case is already supported but only using explicit declarations/customizers
A sample domain is available in the source code in ConfOrmTests.InterfaceAsRelation.Domain.cs
Any help will be appreciated (“help” does not mean “patch”).

Update (2010-11-06): Case-1 supported and tested for many-to-one in simple domain.
Next updates will be available seeing commits.

04 November 2010

ConfORM: understanding polymorphism (interfaces)

The Domain

ConfORMInterfacePolymorphisDemo
Three entities and two components some ones implementing the same interface.

The mapping

  1. var entities = new[] { typeof(User), typeof(Email), typeof(InstantMessage) };
  2. var orm = new ObjectRelationalMapper();
  3. orm.TablePerClass(entities);
  4.  
  5. var mapper = new Mapper(orm);
  6. mapper.Customize<IHasMessage>(x => x.Property(hasMessage => hasMessage.Message, pm => { pm.Type(NHibernateUtil.StringClob); pm.Lazy(true); }));
  7. mapper.Customize<Tweet>(x => x.Property(tweet => tweet.Message, pm => { pm.Type(NHibernateUtil.String); pm.Length(140); pm.Lazy(false); }));
  8.  
  9. var mapping = mapper.CompileMappingFor(entities);
At line 6 you can see the map of the property Message of the interface.
At line 7 you can see exception of the above rule only for the class Twett.

Where is the exception not of a convention but of the persistent representation of the property of an interface is clear, no ?

The XML

If you analyze the below XML you will see the ConfORM’s capability to understand the polymorphism.
<class name="User">
  <id name="Id" type="Int32">
    <generator class="hilo" />
  </id>
  <property name="Name" />
  <bag name="WelcomeMessage">
    <key column="user_key" />
    <composite-element class="UserWelcomeMessage">
      <property name="ForDate" />
      <property name="Message" type="StringClob" lazy="true" />
    </composite-element>
  </bag>
  <bag name="Tweets">
    <key column="user_key" />
    <composite-element class="Tweet">
      <property name="Message" type="String" length="140" />
    </composite-element>
  </bag>
</class>
<class name="Email">
  <id name="Id" type="Int32">
    <generator class="hilo" />
  </id>
  <property name="To" />
  <property name="Cc" />
  <property name="Message" type="StringClob" lazy="true" />
</class>
<class name="InstantMessage">
  <id name="Id" type="Int32">
    <generator class="hilo" />
  </id>
  <property name="To" />
  <property name="Message" type="StringClob" lazy="true" />
</class>


Are you ConfORM ?

P.S. Now is time to work in another level of polymorphism between class-interfaces relations.

03 November 2010

ConfORM: understanding polymorphism (inheritance)

As first a simple example based on inheritance.
private class Entity
{
    public int Id { get; set; }
}

private class VersionedEntity : Entity
{
    public int Version { get; set; }
}

private class MyEntity : VersionedEntity
{
    public string Name { get; set; }
}
The class Entity and the class VersionedEntity are not part of my domain, they are there only because OOP stuff; in practice I don’t want a mapping for those two classes.

To make the matter a little bit more complicated I will add two others classes.
private class TimeVersionedEntity: Entity
{
    public byte[] UpdatedAt { get; set; }
}

private class MyOtherEntity : TimeVersionedEntity
{
    public string Name { get; set; }
}
To let ConfORM to discover which is the property represent the version you can implement a IPattern<MemberInfo> and add it to ObjectRelationalMapper.Patterns.Versions but that is only if you like to work a little bit more than necessary.

In this case the most short way, and the most strongly-typed, is using the ConfORM’s capability to understand the polymorphism. In practice:
var orm = new ObjectRelationalMapper();
orm.VersionProperty<VersionedEntity>(x => x.Version);
orm.VersionProperty<TimeVersionedEntity>(x => x.UpdatedAt);

That done you can continue as usual getting the mappings for your entities; In this case, for instance, it may mean:
var rootEntities = new[] { typeof(MyEntity),typeof(MyOtherEntity) };
orm.TablePerClass(rootEntities);

var mapper = new Mapper(orm);
var mappings = mapper.CompileMappingFor(rootEntities);

If you have to customize the mapping of versions properties, again you can do it class-by-class (working a lot) or again using the ConfORM’s capability to understand the polymorphism:
mapper.Class<VersionedEntity>(cm => cm.Version(x => x.Version, vm => vm.Column("Revision")));

mapper.Class<TimeVersionedEntity>(cm => cm.Version(x => x.UpdatedAt, vm =>
    {
        vm.Column(column =>
            {
                column.Name("LastUpdate");
                column.SqlType("timestamp");
            });
        vm.Generated(VersionGeneration.Always);
    }));

Now suppose that what you need is a special case where for a class inheriting from VersionedEntity, like above, you have to use another column name than “Revision” (like above). What you have to do is only specify your special case trusting in ConfORM capability to understand the polymorphism:
mapper.Class<VersionedEntity>(cm => cm.Version(x => x.Version, vm => vm.Column("Revision")));
mapper.Class<MySpecialEntity>(cm => cm.Version(x => x.Version, vm => vm.Column("Version")));

In this example I shown only a simple usage for the property representing the version for optimistic lock but you can apply the same concept to any property.

Are you ConfORM ?

ConfORMando NHibernate 3 (the day after)


Fue un placer ayer mostrar lo que se puede llegar a hacer con NHibernate 3.0 y alguna otras “cajita feliz”.

La verdad es que pensaba que fuese un poco mas desafiante especialmente por el hecho que la mayoría de los asistentes son actualmente usuarios de NHibernate. En dos horas no pudimos ver mucho porque me dejaron hablar demasiado… Winking smile

Tal vez, si lo piden al MUG, podamos hacer otra session del mismo estilo (a puro codigo from scratch).

El codigo que usamos para jugar está disponible en http://bitbucket.org/fabiomaulo/nhibernatemystic
Nos vemos a la proxima.

16 October 2010

Sharp Tests Ex 1.1.0 RTM

Sharp Tests Ex 1.1.0 RTM was released today.

Mayor changes are related to some internal matters and references to specific framework.

Now you can use SharpTestsEx even to test your Silverlight4 applications.

Download it and : Happy testing!!!

15 October 2010

ConfORMando NHibernate 3

El dia 2 de Noviembre estaré presenciando una charla en el auditorium del MUG (Microsoft Users Group) de Buenos Aires, Rivadavia 1479 1º A.


Ver mapa más grande

La intencción es la de mostrar un uso “distinto” de NHibernate.

Para asistir no se necesita ninguna experiencia previa con NHibernate y si nunca lo han usado verán cuan simple es su uso. Si ya están usando NHibernate, aunque sea desde varios años, verán algo nuevo... no creo que se vayan a aburrir.

El evento es gratuito y pueden registrarse desde aquí.
Los espero.

11 October 2010

TURBO NHibernate with domain invaders

The idea of this post have started at the local Buenos Aries CodeCap 2010 where I have attended : “In loving by Entity Framework”… well at that moment my nice girlfriend was at home. I saw a really nice girl… well… they have shown me her face and a big nice dress that covered the entire body ... I asked to see her in a bikini but it was not possible.
Seriously (I’ll try) the question to myself was: can we take some advantage, from a code generator, creating those fat POCOs ?
I know that some of my posts may seems does not have sense… to find some hidden gems in NHibernate is not so easy ;-)

Puorte 'e cazune cu nu stemma arreto...na cuppulella cu 'a visiera aizata...

public class PocoEntity
{
    public virtual Guid Id { get; set; }
    public virtual string Description { get; set; }
}

Tu vuo’ fa’ ll’americano…sient'a mme chi t' 'o ffa fa'?

public class SelfTrackingEntity : INotifyPropertyChanged
{
    private string description;
    public virtual Guid Id { get; set; }

    public virtual string Description
    {
        get { return description; }
        set
        {
            if (value != description)
            {
                description = value;
                NotifyPropertyChanged("Description");
            }
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

Ok, I have the two entities, and now I can start the proof of concept.
I’ll fill the DataBase with one thousand instances for each entity, then I’ll upload 1000 instances of each, all in one session, I’ll modify few (only 10 entities) and then I’ll flush the session tracking the time of the flush. In other words:

[TestFixtureSetUp]
public void DbCreation()
{
    nhinit = new NHibernateInitializer();
    nhinit.Initialize();
    nhinit.CreateSchema();
    sf = nhinit.SessionFactory;
    FillDb();
}

public void FillDb()
{
    using (var s = sf.OpenSession())
    using (var tx = s.BeginTransaction())
    {
        for (int i = 0; i < 1000; i++)
        {
            s.Persist(new PocoEntity { Description = "pocoValue" + i });
        }
        tx.Commit();
    }

    using (var s = sf.OpenSession())
    using (var tx = s.BeginTransaction())
    {
        for (int i = 0; i < 1000; i++)
        {
            s.Persist(new SelfTrackingEntity { Description = "SelfTrackingEntityValue" + i });
        }
        tx.Commit();
    }
}

[Test]
public void TimeToFlushPocoEntities()
{
    using (var s = sf.OpenSession())
    using (var tx = s.BeginTransaction())
    {
        var entities = s.QueryOver<PocoEntity>().List();
        var someToModify = entities.Skip(4).Take(10);
        foreach (var entity in someToModify)
        {
            entity.Description = "Modified";
        }
        var stopWath = new Stopwatch();
        stopWath.Start();
        tx.Commit();
        stopWath.Stop();
        Console.WriteLine("Milliseconds to flush and commit:" + stopWath.ElapsedMilliseconds);
    }          
}

[Test]
public void TimeToFlushSelfTrackingEntities()
{
    using (var s = sf.OpenSession())
    using (var tx = s.BeginTransaction())
    {
        var entities = s.QueryOver<SelfTrackingEntity>().List();
        var someToModify = entities.Skip(4).Take(10);
        foreach (var entity in someToModify)
        {
            entity.Description = "Modified";
        }
        var stopWath = new Stopwatch();
        stopWath.Start();
        tx.Commit();
        stopWath.Stop();
        Console.WriteLine("Milliseconds to flush and commit:" + stopWath.ElapsedMilliseconds);
    }
}

 

Tu abball' o' rocchenroll tu giochi a baisiboll... ma e solde p' e' Ccamel chi te li dá ?

The result ?

For POCO entities: 72 Milliseconds
For entities with INotifyPropertyChanged : 11 Milliseconds

The difference is really big (auto-dirty-check is 7 times slower), but the unit of measurement is not (milliseconds).

Tu vuo' fa' ll'americano ma si' nato in Italy!

How much code I wrote to have this result ?
what about this ?
  1. [Serializable]
  2. public class PostLoadEventListener : IPostLoadEventListener
  3. {
  4.     public void OnPostLoad(PostLoadEvent @event)
  5.     {
  6.         var trackableEntity = @event.Entity as INotifyPropertyChanged;
  7.         if(trackableEntity != null)
  8.         {
  9.             EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);
  10.             entry.BackSetStatus(Status.ReadOnly);
  11.             entry.BackSetTracer(new EntityTracer(entry, trackableEntity));
  12.         }
  13.     }
  14. }
  15.  
  16. public class EntityTracer
  17. {
  18.     public EntityTracer(EntityEntry entry, INotifyPropertyChanged trackableEntity)
  19.     {
  20.         trackableEntity.PropertyChanged += (sender, e) => entry.BackSetStatus(Status.Loaded);
  21.     }
  22. }
  23.  
  24. [Serializable]
  25. public class PreDeleteEventListener : IDeleteEventListener
  26. {
  27.     public void OnDelete(DeleteEvent @event)
  28.     {
  29.         OnDelete(@event, new IdentitySet());
  30.     }
  31.  
  32.     public void OnDelete(DeleteEvent @event, ISet transientEntities)
  33.     {
  34.         var session = @event.Session;
  35.         EntityEntry entry = session.PersistenceContext.GetEntry(@event.Entity);
  36.         if (entry != null && entry.RowId is EntityTracer)
  37.         {
  38.             entry.BackSetStatus(Status.Loaded);
  39.         }
  40.     }
  41. }

 

sient' a mme: nun ce sta niente 'a fa' ok, napulitan!

This was only a proof of concept, perhaps it has memory leaks, perhaps I have used some nasty tricks… but it works.
Now is the time to apply some very little little changes in NHibernate core to allow a more easy implementation of this and others possible features.
P.S.: the sound track is available here.

08 October 2010

Castle Windsor InstantiateAndForgetIt Lifestyle

Directly to the point.

The case

public interface IMySingleton : IDisposable
{
}

public class MySingleton: IMySingleton
{
    public void Dispose()
    {
    }
}

public interface IMyTransient
{
}

public class MyTransient : IMyTransient
{
    private readonly IMySingleton singleton;

    public MyTransient(IMySingleton singleton)
    {
        this.singleton = singleton;
    }
}
As you can see it is pretty common: I have a class MyTransient with a dependency from a singleton. The singleton is disposable.
The transient is from the point of view of its usage, that mean that its lifecycle is managed by an “external context” and, as is, it does not need a special “destructor”. The “external context” may hold the instance somewhere or may use it only in the context of a method and can quite leave the destruction to the garbage collector. If/when MyTransient implements IDisposable the “external context” should dispose the instance. You can see this behavior in various places for example in the DefaultControllerFactory of Asp.NET MVC or in the IInstanceProvider of WCF (both has a ReleaseInstance cheking for IDisposable); personally I have this situation in other places.
If I do something like this
var myInstance = new MyTransient(container.Resolve<IMySingleton>());
The garbage collector will have zero problems to destroy myInstance and I’ll have zero memory leaks… that is clear, no ?

The test using Windsor with Transient Lifestyle

public class TransientLeak
{
    private WindsorContainer GetContainerConfiguredWindsorContainer()
    {
        var container = new WindsorContainer();
        container.Register(Component.For<IMySingleton>().ImplementedBy<MySingleton>());
        container.Register(Component.For<IMyTransient>().ImplementedBy<MyTransient>().LifeStyle.Transient);
        return container;
    }

    [Test]
    public void WhenTransientRequiredThenReturnDifferentInstances()
    {
        using (WindsorContainer container = GetContainerConfiguredWindsorContainer())
        {
            var t0 = container.Resolve<IMyTransient>();
            var t1 = container.Resolve<IMyTransient>();

            t0.Should().Not.Be.SameInstanceAs(t1);
        }
    }

    [Test]
    public void WhenTransientRequiredThenContainerShouldntHaveInstancesOfMyTransient()
    {
        using (WindsorContainer container = GetContainerConfiguredWindsorContainer())
        {
            var t0 = container.Resolve<IMyTransient>();
            var t1 = container.Resolve<IMyTransient>();

            container.Kernel.ReleasePolicy.Satisfy(rp=> !rp.HasTrack(t0));
            container.Kernel.ReleasePolicy.Satisfy(rp => !rp.HasTrack(t1));
        }
    }     
}
Well… the first test pass, that means that each time I asking for a IMyTransient I’ll have a new instance of the concrete implementation injected with the same instance of IMySingleton (as expected).

Now take care : The second test fails. That means that the container is holding two instances of MyTransient class; let me show you where:
TransientLeak
Perhaps there is a good explication for this behavior but I must admit that I can’t understand why all instances of MyTransient should have its lifecycle stuck to the lifecycle of MySingleton only because MySingleton is disposable… bah?!? by the way that is not a matter because Castle.Windsor give us the ability to define the behavior we need.

The solution

First of all I need my custom lifestyle manager:
[Serializable]
public class InstantiateAndForgetIt : ILifestyleManager
{
    private IComponentActivator componentActivator;

    public void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model)
    {
        this.componentActivator = componentActivator;
    }

    public object Resolve(CreationContext context)
    {
        return componentActivator.Create(context);
    }

    public bool Release(object instance)
    {
        return true;
    }

    public void Dispose()
    {
     
    }
}

Pretty simple but not enough. Then I need an implementation of IReleasePolicy and I can simply inherit from the default and override a method:
[Serializable]
public class LifecycledComponentsReleasePolicy : Castle.MicroKernel.Releasers.LifecycledComponentsReleasePolicy
{
    private readonly Type instantiateAndForgetItType = typeof (InstantiateAndForgetIt);

    public override void Track(object instance, Burden burden)
    {
        if (instantiateAndForgetItType.Equals(burden.Model.CustomLifestyle))
        {
            return;
        }
        base.Track(instance,burden);
    }
}

The last step is the modification of the container’s configuration:
private WindsorContainer GetContainerConfiguredWindsorContainer()
{
    var container = new WindsorContainer();
    container.Kernel.ReleasePolicy = new LifecycledComponentsReleasePolicy();
    container.Register(Component.For<IMySingleton>().ImplementedBy<MySingleton>());
    container.Register(Component.For<IMyTransient>().ImplementedBy<MyTransient>().LifeStyle.Custom<InstantiateAndForgetIt>());
    return container;
}


Work done!! and bye bye “memory leaks”.


Update: request to have InstantiateAndForgetIt Lifestyle natively supported issue IOC-225 (vote for it)

05 October 2010

NHibernate 3.0 Cookbook.

This week Packt published the NHibernate 3.0 Cookbook. Packt are offering all members of the NHibernate community 20% off the book.
The book helps users to get solutions to common NHibernate problems to develop high-quality performance-critical data access applications.
The book, which is 328 pages long, contains quick-paced self-explanatory recipes organized in progressive skill levels and functional areas.
Overview of NHibernate 3.0 Cookbook
  • Master the full range of NHibernate features
  • Reduce hours of application development time and get better application architecture and performance
  • Create, maintain, and update your database structure automatically with the help of NHibernate
  • Written and tested for NHibernate 3.0 with input from the development team distilled in to easily accessible concepts and examples
  • Part of Packt's Cookbook series: each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible
Is this book for you?
This book is written for NHibernate users at all levels of experience. Examples are written in C# and XML. Some basic knowledge of SQL is assumed.
To get your exclusive 20% discount when you buy through PacktPub.com, just enter the discount code NHIBCBK20 (case sensitive), to the shopping cart.
Click here to read more about the NHibernate 3.0 Cookbook.

06 September 2010

ConfORM: Usage Examples

I have promised myself to no post often about ConfORM, instead I have started a new project, available with ConfORM’s sources, where I’ll show some usage examples with some explication.

So far there are few examples created under users requests. If you want see the code without download, it is available here.

Taking the opportunity of this post I would show you another success story:
It is about an “Inventory management and stock control” system composed by 65 entities/components where the persistence is managed using NHibernate 2.1.2. Because ConfORM uses a NHibernate 3 specific feature they can’t use ConfORM directly in the application, instead they are using ConfORM to generate XML mappings (soon, probably, they will use it directly with NHibernate 3). They can trust in ConfORM because they have, so far, 83 persistence integration-tests testing CRUD and expected cascades operations.

I can’t show you the domain-model but I can show you the whole mapping:

IEnumerable<Type> domainEntities = typeof (BaseEntity)
    .Assembly.GetTypes()
    .Where(t => typeof (BaseEntity).IsAssignableFrom(t) && !t.IsGenericType)
    .ToList();

var tablePerClassHierarchy = new[] {typeof (Movement), typeof (MovementDetail)};
IEnumerable<Type> tablePerClassEntities = typeof (BaseEntity)
    .Assembly.GetTypes()
    .Where(t => typeof (BaseEntity) == t.BaseType && !tablePerClassHierarchy.Contains(t))
    .ToList();

var orm = new ObjectRelationalMapper();

orm.TablePerClass(tablePerClassEntities);
orm.TablePerClassHierarchy(tablePerClassHierarchy);

orm.Exclude(typeof (Movement<>));
orm.Exclude(typeof (MovementDetail<>));

orm.ManyToMany<Product, Tag>();

var patternsAppliers =
    (new SafePropertyAccessorPack())
        .Merge(new OneToOneRelationPack(orm))
        .Merge(new BidirectionalManyToManyRelationPack(orm))
        .Merge(new BidirectionalOneToManyRelationPack(orm))
        .Merge(new DiscriminatorValueAsClassNamePack(orm))
        .Merge(new CoolTablesAndColumnsNamingPack(orm))
        .Merge(new TablePerClassPack())
        .Merge(new DatePropertyByNameApplier())
        .Merge(new MsSQL2008DateTimeApplier());

var mapper = new Mapper(orm, patternsAppliers);

mapper.Customize<Product>(
    cm =>
        {
            cm.Property(product => product.Code, pm =>
                                                     {
                                                         pm.NotNullable(true);
                                                         pm.Unique(true);
                                                     });
            cm.Collection(product => product.Tags, tagsm => tagsm.Cascade(Cascade.Persist));
        });

mapper.Customize<Role>(
    cm => cm.Collection(role => role.Functionalities, com => com.Cascade(Cascade.Persist)));

mapper.Customize<User>(cm => cm.Property(user => user.Nick, n => n.Unique(true)));

In this mapping you can see how compose ConfOrm.Shop.Packs and how map a whole domain in few lines ;-)

Are you ConfORM ?
They are!!!

27 August 2010

ConfORM 1.0Alpha2

I’m happy to announce the first release of ConfORM binaries.

The release is tagged as Alpha2 just because we are using it in production, in four projects, since 3 months now (at least).

Thanks to JetBrains and CodeBetter you can inform your issues through YouTrack.

Welcome to ConfORM!!
There are times when a simple piece of code seems to have a soul.

Sound track

29 July 2010

NHibernate LINQ provider extension

In NHibernate almost all OO query systems (HQL, Criteria, QueryOver) are extensible. LINQ is, in its nature, a big extensible extension, but can you extend our LINQ-provider to translate your LINQ-extension in SQL ?
As usual in NHibernate, the answer is : yes, you can!!

The LINQ extension

As example I’m going to use an extension of string to mimic the SQL LIKE clause.
public static class MyLinqExtensions
{
    public static bool IsLike(this string source, string pattern)
    {
        pattern = Regex.Escape(pattern);
        pattern = pattern.Replace("%", ".*?").Replace("_", ".");
        pattern = pattern.Replace(@"\[", "[").Replace(@"\]","]").Replace(@"\^", "^");

        return Regex.IsMatch(source, pattern);
    }
}
I can use my extension in memory but what I need is instruct NHibernate about its translation for my persistence-queries.

The integration

The integration point is a class implementing : ILinqToHqlGeneratorsRegistry
The LinqToHqlGeneratorsRegistry provides the way to recognize and translate methods and properties of varios classes. Our DefaultLinqToHqlGeneratorsRegistry can manage LINQ extensions as Any, All, Min, Max etc., methods as StratsWith, EndsWith, Contains, IndexOf etc. or properties as Year, Month, Day, Length etc.
First of all I have to implements a IHqlGeneratorForMethod. NHibernate provides a cooked base class and my implementation will look as:

public class IsLikeGenerator : BaseHqlGeneratorForMethod
{
    public IsLikeGenerator()
    {
        SupportedMethods = new[] {ReflectionHelper.GetMethod(() => MyLinqExtensions.IsLike(null, null))};
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
        ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(),
                                visitor.Visit(arguments[1]).AsExpression());
    }
}

Having the HQL generator for my extension I can implements my ILinqToHqlGeneratorsRegistry extending the default implementation.

public class MyLinqToHqlGeneratorsRegistry: DefaultLinqToHqlGeneratorsRegistry
{
    public MyLinqToHqlGeneratorsRegistry():base()
    {
        RegisterGenerator(ReflectionHelper.GetMethod(() => MyLinqExtensions.IsLike(null, null)),
                          new IsLikeGenerator());
    }
}

The configuration

The last step to use my IsLike extension to query the DB is the configuration of my LinqToHQLGeneratorsRegistry.
The name of the property for the XML configuration is : linqtohql.generatorsregistry
In NHibernate 3 you can use the Loquacious-configuration :

configuration.LinqToHqlGeneratorsRegistry<MyLinqToHqlGeneratorsRegistry>();

 And now...

var contacts = (from c in db.Customers where c.ContactName.IsLike("%Thomas%") select c).ToList();

Welcome to the world of options,
Welcome to NHibernate3!!!