EntityFramework 4.1 RC Code first; review

This is my experience with EF 4.1 Code First so far. It may be useful for you given my background in NHibernate.

I am not trying to start a rant against it. Just to highlight the state of the art; and how you might overcome to some problems.

The domain I tried to map was this:

2011-03-29_2015

Four classes, Two enums.

I just want to keep this clear; I will talk about very simple cases. I know that I can handle really really convoluted scenarios with NHibernate as ORM but I will talk about the most common scenarios I have seen.

Swallowing exceptions

This is one of the most irritating things. I’ve seen two cases for this so far:

  • If you don’t set a connection string or your connection string name is wrong. EntityFramework doesn’t throw. Instead it creates a new database in the sqlexpress instance; named with the full namespace of the DbContext. Accessing with trusted connection of course. I’d expect rather something like this:

2011-03-29_2026

  • If you have sealed properties; everything will work. It is even going to save an object with a many-to-one relationship with the right values in the database and it will load the object back. Surprisingly the reference will be null instead of a proxy (this is obviously for lazy loading stuff). NHibernate will throw by default an exception given you a list of the sealed methods;

2011-03-29_2034

Of course you can disable this to speed up the built of the session factory but that is pointless because it is supposed to happen once.

In my humble opinion they should have more care with those things if you don’t want to annoy developers with odd results.

Possible workaround

There is not; this is EF internals. If you don’t want to deal with “virtual” thing and make everything virtual you should definitely take a look to Virtuosity; a project from my friend Simon.

Logging facilities

There is no way to have a look what commands are going to the database. This is widely used in the NHibernate world and you have two ways by simply using show_sql = true or using a logging framework like log4net.

Possible workaround

There are some hacks; like wrapping the database connection provider; as explained here but it didn’t work for me. The other thing you can do is to use EfProf, this is a commercial product and serve for a wide range of purposes.

Lack of good identifiers generators

EntityFramework support two kind of Persistent Object ID generation techniques; identity and assigned.  NHibernate 2.1 have 17 techniques for generating your POID (article from 2009) and  the two most discouraged patterns are; identity and assigned. This is not a minor thing for an ORM:

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.

At some point this means that you will be sending the operations as a batch when flushing. But “identity” in sql server is a bad strategy because you don’t know the ID before inserting the record; you need to insert the record and then you can ask for the ID (or the last id). Even Sequences (in most of the DBMS) work better because you can pull numbers without inserting anything.

Keep in mind that EntityFramework work quite different than NHibernate regarding this:

  • NHibernate ensure than the Save method (before flushing to the database) will put an ID in the entity, so you can operate with the entity ID before flushing the changes. This is very important when you use a pattern like session-per-request; because you will probably use the ID for something else like generating a link.
  • EntityFramework ensure the unit of work works as unit and will not give you the ID before flushing the changes.

What is the best? I am not sure because identity is a very bad thing despite how you use it. It is not an ORM problem is just bad and any DBA will agree on this.

Posible workarounds

This is easy to fix in the current status of EntityFramework. Don’t use identity:

modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();

and EntityFramework will think that you are going to use assigned. From here you have two paths:

  • If you don’t want to generate human-readable values; stick to GUID and use something like this in the constructor of the entity:
public EntityBase
{
  public EntityBase()
  {
     Id = Guid.NewGuid()
  }
  public Guid Id { get; set; }
}

We are generating a Guid in the constructor; every entity will have an Id before flushing and… when you load an entity EF will put the right id (that comes from the database) in the ID property after constructing the instance.

  • If you want to generate human-readable values; I encourage you to use the HiLo algorithm. Fortunately I write a post some days ago. I been working in this and I’ve enhanced the design of that classes but I will not publish yet until I get something more mature.

Lack of customizable mapping types

NHibernate has a really important extension point; the interface IUserType. I use at least one custom implementation in every project. What does this interface? Is pretty simpler; It allows you to describe how a value will be read and write to/from the database.

Let’s say that you are working on legacy database and it uses “N” and “S” (from Spanish “si” = “yes”). You can map this to a char property on .Net but it is not right because the true meaning of this in the c# language would be “System.Boolean”. So NHiberante allows you to decouple the database implementation from the object oriented implementation.

The same thing is used for mapping enums to the database. NHibernate comes with a handy collection of useful user types.

There is another interface named ICompositeUserType that allows to map a property to multiple columns in the database; it is not widely used/known though.

Possible workarounds

It is ugly, but for mapping enums I did something like this:

#region EntityFramework Specific
[Obsolete("EntityFramework can't map enums.")]
public virtual string StatusString
{
    get { return Status.ToString(); }
    set
    {
        OrderStatus newValue;
        if (Enum.TryParse(value, out newValue))
        {
            Status = newValue;
        }
    }
}
#endregion

public virtual OrderStatus Status { get; set; }

Lack of Read-Only properties

ReadOnly properties in NHibernate means than the value is going to be saved and updated in the database but it will never be read from the database because it is a read-only property:

public decimal Total
{
  get{
    Items.Sum(i => i.Quantity * i.Price);
  }
}

There is a full explanation here, from my friend Germán Schuager.
This most of the time means a de-normalization of the database; but it is quite right because you will be saving the total of an invoice almost always for sure.

Possible workaround

Carlos Peix illuminated me in twitter:

2011-03-29_2132

Yes! Carlos you were right:

public decimal Total
{
  get{
    Items.Sum(i => i.Quantity * i.Price);
  }
  set{ 
     // no op 
  }
}

we can fool EntityFramework this way.

Lack of collection of Elements

This means that EntityFramework doesn’t currently support ISet<string>, ICollection<string> or ISet<int> as property. NHibernate support this from earlier versions. It will simply use another table for storing the elements. You can have even collection of enums because it will use the same mechanism for every type; a plain IUserType as explained above.

NHibernate also support collection of components; entities without meaning outside like Address or telephone.

Possible workaround

I’ve used for the above domain some kind of de-normalization and serialization/parsing;

[Obsolete("EntityFramework doesn't store collection of elements.")]
public virtual string CommaSepparatedPossibleValues
{
    get { return string.Join(",", possibleValues); }
    set
    {
        if(value == null) return;
        value.Split(',')
             .ToList()
             .ForEach(v => possibleValues.Add(v));
    }
}

Lack of MAP mapping or Dictionary mapping

The term “map” come from the java world; while in .Net map means exactly Dictionary. NHibernate can handle a widely variety of dictionaries:

  • Element as Key, Element as Value
  • Element as Key, Component as Value
  • Element as Key, Entity (many-to-many) as value
  • Component as Key, Element as Value
  • Component as Key, Component as Value
  • Component as Key, Entity (many-to-many) as value
  • Entity (many-to-many) as Key, Element as value
  • Entity (many-to-many) as Key, Component as value
  • Entity (many-to-many) as Key, Entity as value

This is very well explained here. 

Possible workaround

There isn’t. The domain has only one case of IDictionary<string, string>. I handle with something very similar to the previous workaround.

The cost of the workarounds

Most of these workarounds come with a price.

  • You will not be able to query in those properties by sure.
  • You will pollute your entities with weird properties, like an empty set (in a read-only property) or two properties for converting something.

Non-technical issues

Lack of documentation

There is a bad combination between long release cycles with a product that is pushed by a big company and has lot of people around writing articles on alphas, betas and so on. If you do a google search for something you will find a variety of API that are not currently supported. You can try this right now do a search for:

  • disable identity generator
  • generating the database schema
  • mapping a many to many

I can’t really understand why people say than this is a pro on EntityFramework right now. Even the MSDN documentation doesn’t match the current programming API.

On the other hand NHibernate has a good reference documentation and lot of posts with working examples. In fact; when lurking for a problem sometimes I found the answer in the hibernate reference documentation or even in the hibernate forums!. Is NHibernate exempt of this kind of problem? definitely no for instance NHibernate has a vocabulary that doesn’t always match with the FluentNHibernate ones; mutable=”false” in nhibernate is “ReadOnly()” in FNH. But is pretty common from bloggers to mark something as outdated. Another example is that for instance even if we have like 5 official different ways of mapping a class if you ask a question in the forum you know that you should send the official dialect: plain old xml (aka POX).

Ignoring the progress of the OpenSource community

The NHibernate/Hibernate community has decades dealing with this. Ignore them it is not the best thing.
I thought Microsoft had overcome to this;

  • Mef was one of the first in codeplex
  • Asp.Net MVC is OSS
  • The new WCF web api is OSS
  • NuGet is the new guy on the block

While EF team is trying to figure out how to create a good fluent API; NHibernate has a production-ready API since long time that work for a lot of projects ( FluentNHibernate ). It has two attribute based APIS NHibernate.Mapping.Attributes and Castle.ActiveRecord. It has a convention based API more powerful than the one it comes out of the box with EF; -because you can actually define conventions- ( FluentNHibernate AutoMappings ). And now it has a another convention based API that can read your mind; it has set of powerful conventions for inferring your mappings via reflection and a powerful set of conventions to make your DBA happy ( ConfORM ).

So, my two cents on this; is to keep an eye in the community. There are lot of clever people willing to help.

I hope you find this article interesting if you are coming from NH to EF or if you are still in NH to get an idea how are going things in EF.

If you find something wrong in one of the points made here; don’t hesitate to send me a comment, tweet, mail or whatever.


blog comments powered by Disqus
  • Categories

  • Archives