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.