BrainHzSoftware.com

Real world Entity Framework

Posted by Scott Reed on 22nd January 2009

[Update: this refers to the 1.0 version found in .NET 3.5]

Sure, I had read the disclaimer here: http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/

But these were the requirements:

  • Needed to support multiple databases
    They had already switched databases once (from Oracle to SQL Server) and they still had some other databases in Oracle
  • Could not use strings (had to be type-safe)
    This was due to the amount of change (we were developing a fairly complicated v1.0 product)

When we initially started the project back in early 2008 (a year or so ago) we had a meeting with four people to try and choose an ORM. The first decision made was we agreed that NHibernate was out, because it uses strings in its query syntax, and the support for generation isn’t on par with the others. We discussed many aspects as to how the remaining choices are used. One of the most important points was whether they had a good online community, but all of them met that bar. Here is a summary of the differences:

Consideration Linq to SQL Entity Spaces Entity Framework
Released Product Yes Yes No
Supports LINQ Yes No Yes
Clean business objects Yes No Yes
Easy query navigability for Many-to-manys No Yes Yes
Easy to modify Many-to-manys No No Yes
Supports all databases No Yes Yes

Ultimately it was decided that as long as we are careful it should be easy to convert between the DALs. That way we can start of in one and move to another one. We will start off with Entity Spaces. One additional action item was to check out LLBLGen Pro, and msbuild support for MyGeneration.

Now that 3.5 SP1 had finally shipped we decided to completely replace our data access layer with Entity framework. One thing that we didn’t like going in was the fact that we still wouldn’t be able to replace our existing two-tiered entity objects. In our initial model we have one set tied to Entity Spaces and the other which is a DTO representing basically the same object.

What we liked:

  1. We could use LINQ
    I know that it doesn’t seem like this is such a big deal, but check this out:

    Before:

    public static ContainerInfo[] GetContainerInfosByExternalId(
    	string externalSampleId)
    {
        var containerInfos = new List<containerinfo>();
        if (String.IsNullOrEmpty(externalSampleId))
            return new ContainerInfo[0];
        var sampleSlotQuery = new SampleSlotQuery("s");
        Dal.InitConnectionName(sampleSlotQuery.es);
        var containers = new ContainerCollection();
        containers.Query.es.JoinAlias = "c";
        Dal.InitConnectionName(containers.es);
    
        containers.Query.InnerJoin(sampleSlotQuery)
           .On(containers.Query.ContainerId == sampleSlotQuery.ContainerId);
        containers.Query.Where(sampleSlotQuery.ExternalSampleId == externalSampleId);
        containers.Query.OrderBy(containers.Query.ContainerId.Ascending);
        if (containers.Query.Load())
        {
            foreach (Container container in containers)
            {
                containerInfos.Add(ConvertToContainerInfo(container));
            }
        }
        return containerInfos.ToArray();
    }
    

    After:

    public static ContainerInfo[] GetContainerInfosByExternalId(
    	WorkflowStateEntities db, string externalId)
    {
        return
            (from c in db.Containers.Include("Samples")
             from ss in c.SampleSlots
             where ss.ExternalId == externalId
             select ConvertToContainerInfo(c)).ToArray();
    }
    

What we didn’t like. These were relatively minor things like:

  • No lazy loading
  • Non type-safe eager loading
  • (See here)

It took 5 of us about 4 days to convert the entire thing over. After it was done I tried to figure out how many lines we were able to remove. However, there was a problem, because even though I knew we were able to reduce the code, the Code Metrics in Visual Studio showed that the number of lines increased. After some frustration, I finally used a version of wc (the Unix utility) which showed a 10-15% reduction across the board

Another problem we had was the varchar / nvarchar problem. We actually reported this problem to the Entity Framework team through one of our Help support tickets. From one of our developers
In EF, as it turns out, the queries are ALWAYS cast to nvarchar (Unicode) regardless of the schema column type being a varchar (or bit). Before running my last set of traces (those shown above), I made sure that ALL columns in the schema were preset to be nvarchar types before running the EF code. That actually improved the query speed, cutting the time in half from about 4000 microseconds to 2200 microseconds

But the biggest problem turned out to be performance. We had some pretty big load operations going in. By converting one of these queries went from 21 seconds in Entity Spaces to 2 minutes in Entity Framework. Basically a 6x factor across the board. This performance was not acceptable for our product. So after doing all of the conversion we had to roll everything back – uggh!

Tags: , ,
Posted in ORM | Comments Off