DevelopMENTAL Madness

Wednesday, February 25, 2009

Entity Framework: Abstracting ObjectContext

Wither you are a TDD purest or not, if you are developing with Entity Framework and testability is important to you, then you have tried some workaround or applied some pattern to make testing possible in your project. When you have a class or method which depends on any external resource it is important to create some sort of abstraction in order to remove a dependency upon that resource so you can effectively test your code.

The client I am currently working for has chosen to create a repository class which takes an ObjectContext argument in the constructor. The repository implements a custom interface and that interface is used to reference the repository where needed.

While this meets our needs I was late to the game when I was brought on, so when I suggested an alternative I was praised for my suggestion but because there was already an implementation we ultimately stuck with what we already had. So I decided just to make a quick post describing my own solution and touching on what I see as the benefits.

I (heart) Partial Classes

My ideal framework is open, extensible and makes abundant use of public interfaces which allow me to hook in where I need to so I can get the job done. The team I would say has done the best job of this would be the ASP.NET MVC team. I’m a bit picky about my ORM solutions and I just don’t feel like LINQ2SQL and Entity Framework have quite done everything the way I like. And the ability to extend or change the behavior of Entity Framework seems to be pretty lacking. But there is one thing that I have found to be invaluable as I have worked with these two ORM frameworks – Partial Classes.

Open any *.designer.cs file generated by either LINQ2SQL or Entity Framework and you will see an abundant use of the C# keyword “partial”. While it doesn’t allow me to modify existing Entity Framework behavior I can certainly add to it.

Partial classes are what make it possible for me to simply and elegantly create an abstraction of my ObjectContext without bending over backwards.

The Abstraction

So as an example, I have an AdventureWorks database and I want to abstract the associated ObjectContext created by my edmx designer. To simplify things I’ll simply referece the Person and Address tables. So after finishing with my edmx file, dragging Person and Address to the designer surface I’ll create an interface which looks like this:

public interface ICustomRepository : IDisposable
{
    IQueryable<Person> People { get; }
    IQueryable<Address> Addresses { get; }
    
    Int32 SaveChanges();
 
    void Detach(Object entity);
    void Attach(IEntityWithKey entity);
 
    void DeleteObject(Object entity);
    void AddObject(String entitySetName, Object entity);
}

Because of the difficulty of implementing ObjectQuery<T>, I like to expose the entity set properties on my ObjectContext as IQueryable<T>. In most cases this is sufficient, where it isn’t I can check to see if the IQueryable is of type ObjectQuery. If not then I know I’m in a testing scenario and I can safely skip what I was about to do. Here’s an example of an extension method I’ve created which allows me to use eager loading on my IQuerable interface.

public static IQueryable<T> Include<T>(this IQueryable<T> query, string path)
{
    ObjectQuery<T> q = query as ObjectQuery<T>;
    if (q == null)
        return query;
 
    q = q.Include(path);
 
    return q;
}

Once I’ve added a read-only (property get) for each entity set in my context, then I include the signature for each ObjectContext method I will be using in my application. If I need additional ones down the road, all I need to do is add the signature to the interface and I’m off and running. So I’ve included SaveChanges, Detach, Attach, DeleteObject and AddObject. Feel free to include others you need they’re free and you don’t need to provide any implementation.

The Implementation

Now that we have an interface that can be used throughout our application in place of our ObjectContext, how do we use it to remove our dependency on ObjectContext? Let’s say for argument’s sake our ObjectContext is named AdventureWorksModel. Then we would add a class file to our project which contains our edmx file and in that class file we would define a new class like this:

public partial class AdventureWorksModel : ICustomRepository
{
    IQueryable<Person> ICustomRepository.People 
    { 
        get 
        { 
            return this.People; 
        } 
    }
    IQueryable<Address> ICustomRepository.Addresses 
    { 
        get
        { 
            return this.Addresses; 
        } 
    }
}

First, notice the property names (ex. ICustomRepository.People). Because the properties exposed by the designer generated class are of the type ObjectQuery we have to provide an “explicit implementation” of our interface for those properties. No casting is required, but the compiler and the runtime are looking for properties of type IQueryable and so unless you include the name of the interface in the property name it won’t compile. But because ObjectQuery implements IQueryable, that’s all you have to do. Now you can reference ICustomRepository anywhere and you’re good to go.

Usage

There are any number of ways you can provide access to your repository. The easiest, in my view is using an IoC container like Unity or Spring. Here’s an example of using Unity to register your ObjectContext and resolving it from your container:

// put this in your application's startup event
Container.Register<ICustomRepository, AdventureWorksModel>();
 
// get a reference to your repository anywhere in your application
using(ICustomRepository repository = Container.Resolve<ICustomRepository>())
{
    var result = from p in repository.People
                    where p.Addresses.Any( a => a.State = "CA" )
                    select p;
    
    // do something
}

Once you’ve registered your ObjectContext in the container your application can get a reference to it anywhere through the custom interface. Everything remains testable and there’s no need for any awkward machinations to use it.

Mocking the Abstraction

Now when you’re testing you can write your own mock objects or use your favorite mocking framework (I really like MOQ) to provide an implementation of your interface that isn’t dependent upon your database.

[TestClass]
public class DependentObjectTest
{
    ICustomRepository _repository;
    
    [TestInitialize]
    public void Init()
    {
        List<Person> people = new List<Person>();
        List<Address> addresses = new List<Address>();
        
        Moq.Mock mock = new Mock<ICustomRepository>();
        mock.ExpectGet(r => r.People).Returns(people);
        mock.ExpectGet(r => r.Addresses).Returns(addresses);
        mock.Expect(r => r.AddObject("People", It.IsAny<Object>()))
            .Callback<string, Person>((s, p) => people.Add(p));
        mock.Expect(r => r.AddObject("Addresses", It.IsAny<Object>()))
            .Callback<string, Person>((s, a) => addresses.Add(a));
            
        _repository = mock.Object;
    }
    
    [TestMethod]
    public void Test1()
    {
        Person p = new Person();
        _repository.AddObject("People", p);
        Assert.AreEqual<int>(1, _repository.People.Count);
    }
}

While this isn’t a realistic test scenario because it is only testing the mock object, it should give you an idea of how to mock up an implementation of the interface for use in a test scenario.

If you’re using Unity or something like it then in your TestInitialize method you’ll just change the last line to this:

Container.Register<ICustomRepository, AdventureWorksModel>();

Then your test method won’t need to reference it since the methods you’re testing will get the reference via the Container object.

Conclusion

Thus far, I have found this is a simple and elegant solution which works just like using ObjectContext in any scenario.

Labels: , ,

Tuesday, February 24, 2009

Entity Framework: How to Prevent Eager Loading

As we have been using Entity Framework on my current project I have found that it can be easy to kill eager loading on your queries. This can be a real problem if you are counting on it. I have been working on cross-cutting concerns up to this point, but because of my experience with Linq 2 SQL I was asked about some issues our team was having with eager loading. Namely, the Include() method, which is a method of the ObjectQuery type. If you’ve read any documentation on eager loading I’m sure you’ve read that using projections precludes the use of the Include method. That is you can use it, but it will be ignored (no errors), and you will be left scratching your head. I found we were doing two things which were killing eager loading, which I believe are tied to projections.

Eager Loading

Before I mention the 3 issues I’ve seen to this point, I wanted to preface the list with a summary of how eager loading works.

In order to get the benefit of eager loading you need to, at minimum, define an association between your entities. This is done either in the designer or in your csdl (class structure element of your entity definition file (edmx). When you first create your model, you get this for free if you have defined foreign key constraints in your data model (RDBMS). If you have then the designer will automatically add navigation properties for each entity and an association between the two.

If you want Entity Framework to eager load related entities then you simply call the Include method of a ObjectQuery instance and pass in the name of the NavigationProperty for the association you want included:

using(AdventureWorksModel ctx = new AdventureWorksModel())
{
    var results = from p in ctx.Persons.Include("Addresses")
                    select p;
                    
    List<Person> customers = results.ToList();
}

Once you execute your query Entity Framework all the data, including the data for the association, will be loaded into a collection for you. When you reference the navigation property of an entity for the specified association you’ll find the data is there waiting for you. As long as the Navigation Property exists and you’ve typed the name correctly this will work.

Eager Loading Anti-Patterns

So what can get in the way of eager loading? A couple things:

  1. As I mentioned above, projections do not support eager loading. I believe this is at the root of the issue if you’re experiencing problems with eager loading. Why don’t projections allow for eager loading? For the simple reason that projections create anonymous types. If you aren’t projecting an anonymous type then it’s because the type you are projecting to isn’t in your entity model. Your entity model includes ssdl (storage definition) and mdl (mapping definition) which allows your entity to be mapped to a SQL query. Without these pieces you get nothing. If you need to do a projection of some sort, but you want to include eager loading then see this article which uses projection to include the related data as properties of the projection.
  2. The first thing I ran into was casting to a subclass. With Entity Framework you can mark an entity as an abstract class and inherit from it. Then our LINQ query was casting the “select” clause to a subsclass like this:
    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                  where ps is Customer
                  select ps as Customer;

    I haven’t figured out yet what this is doing under the covers, but I’m guessing this is resulting in a projection, if not something very close to one. Here I am querying an EntitySet of type Person for a Customer type. While the Entity Framework supports inheritance like this, I can see how this would be treated like a projection because of the conversion required. Instead of doing this, I’ve found you can work around the issue by refraining from using the cast in the select statement. Instead cast the result of your query after it has been materialized.

  3. Joining two tables in a query will kill eager loading. It would seem to me that the reason this breaks is because when you join like this it is unreliable. If you were to run this query against a one-to-one relationship you’d be fine. But in any other scenario this breaks down because unless you’re using the DISTINCT clause, things get sloppy.
    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                    join a in EntityModel.Addresses on ps.PersonID equals a.PersonID
                  where ps is Customer && a.State == "CA"
                  select ps;
     

    So Unless you need to create a projection with the results, in which case you can’t use eager loading anyway, then try and use subquerys (Any()).

    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                  where ps is Customer && ps.Addresses.Any( a => a.State == "CA" )
                  select ps as Customer;

Conclusion

So to recap, make sure you have defined navigation properties where you want to enable eager loading and that your arguments to the Include method are spelled the same as your navigation properties. Then avoid anything that Entity Framework would construe as an implicit (or explicit) projection. There may well be other reasons eager loading breaks down but this should be a start.

Labels:

Tuesday, February 10, 2009

Entity Framework: Connection Strings

<add name="AdventureWorksEntities" 
    connectionString="metadata=.\AdventureWorks.csdl|.\AdventureWorks.ssdl|.\AdventureWorks.msl;
    provider=System.Data.SqlClient;provider connection string='Data Source=localhost;
    Initial Catalog=AdventureWorks;Integrated Security=True;Connection Timeout=60;
    multipleactiveresultsets=true'" providerName="System.Data.EntityClient" />

According to MSDN, the above EntityClient connection string will search in the following locations for the specified csdl, ssdl and msl resource files:

  1. The calling assembly
  2. The referenced assemblies
  3. The assemblies in the bin directory of an application

However, I have not found this to be the case. When my edmx file is in an assembly which is external to my project, for example a test project or web project, I get the following error:

System.Data.MetadataException: The specified metadata path is not valid. A valid path must be either an existing directory, an existing file with extension '.csdl', '.ssdl', or '.msl', or a URI that identifies an embedded resource.

And the stack trace includes the following path:

System.Data.Metadata.Edm.MetadataArtifactLoader.Create(String path, ExtensionCheck extensionCheck, String validExtension, ICollection`1 uriRegistry, MetadataArtifactAssemblyResolver resolver)
System.Data.EntityClient.EntityConnection.SplitPaths(String paths)
System.Data.EntityClient.EntityConnection.GetMetadataWorkspace(Boolean initializeAllCollections)
System.Data.Objects.ObjectContext.RetrieveMetadataWorkspaceFromConnection()
System.Data.Objects.ObjectContext..ctor(EntityConnection connection, Boolean isConnectionConstructor)
System.Data.Objects.ObjectContext..ctor(String connectionString, String defaultContainerName)

Fortunately, the documentation also includes the syntax for explicitly referencing the resource files located in an external assembly. I’ve played around with a few different versions of this and this is the minimum information required in an EntityClient connection string:

<add name="AdventureWorksEntities" providerName="System.Data.EntityClient" 
    connectionString="metadata=res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.csdl
    |res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.ssdl
    |res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.msl; 
    provider=System.Data.SqlClient;provider connection string='Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True;multipleactiveresultsets=true'"/>

This connection string assumes your assembly is named MyCompany.Data.dll and the full name of your class is MyCompany.Data.Models.AdventureWorks.

I’m sure I’ll need this myself in the future. I hope it helps you as well.

Labels: ,

Tuesday, January 20, 2009

Entity Framework POCO Adapter + System.Web.DynamicData == POCO Loco

This month I started with a new client. I'm excited because we'll be rewriting a legacy app using Entity Framework against an Oracle backend, and possibly ASP.NET MVC. The team is full of good, experinced developers, any of which could lead their own team. 

But as with all new technologies we're experiencing pains with early adoption. One of design goals is to be testible. We're not quite talking TDD here. None of us have been on a TDD project here so we're more just talking about trying to move in a direction we think will be beneficial. 

Additionally, the project specifications require us to store meta data about our data model and create a relationship between the meta data and the users' roles. The relationship will tell us which fields a user can view or edit based on their role. So we're looking into wither we can use System.Web.DynamicData.MetaModel to collect the meta data about our model by using the MetaModel.RegisterContext method. 

Because we'd like to make as much of this testible as possible we decided to try Jaroslaw Kowalski's POCO Adapter to allow our data layer to be more testible. So as an exercise I fired up a new project, added a new ADO.NET Entity Data Model (.edmx) file and created a data model using the AdventureWorks database. Then I used the EFPocoClassGen utility to create my POCO classes, container and adapter based on my edmx file. Here's the Pre-build event commands in my project:

"$(SolutionDir)EFPocoClassGen.exe" /mode:PocoClasses /inedmx:"$(ProjectDir)Data\AdventureWorks.edmx" /outputDir:"$(ProjectDir)Data"

"$(SolutionDir)EFPocoClassGen.exe" /mode:PocoContainer /inedmx:"$(ProjectDir)Data\AdventureWorks.edmx" /outputfile:"$(ProjectDir)Data\PocoContainer.cs"

"$(SolutionDir)EFPocoClassGen.exe" /mode:PocoAdapter /inedmx:"$(ProjectDir)Data\AdventureWorks.edmx" /outputfile:"$(ProjectDir)Data\PocoAdapter.cs"

Then I added the RegisterContext code to my global.asax:

var model = new MetaModel();
model.RegisterContext(typeof(AdventureWorksEntitiesAdapter),
new ContextConfiguration { ScaffoldAllTables = true });

I ran my project and was greeted by with yellow screen of death bearing this message:

Error: An item with the same key has already been added.



[ArgumentException: An item with the same key has already been added.]
System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) +51
System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) +7460188
System.Data.Metadata.Edm.AssemblyCacheEntry.LoadRelationshipTypes(LoadingContext context) +2061
System.Data.Metadata.Edm.AssemblyCacheEntry.LoadTypesFromAssembly(LoadingContext context) +17
System.Data.Metadata.Edm.AssemblyCacheEntry.InternalLoadAssemblyFromCache(LoadingContext context) +244
System.Data.Metadata.Edm.AssemblyCacheEntry.LoadAssemblyFromCache(Assembly assembly, Boolean loadReferencedAssemblies, Dictionary`2 knownAssemblies, Dictionary`2& typesInLoading, List`1& errors) +137
System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyFromCache(ObjectItemCollection objectItemCollection, Assembly assembly, Boolean loadReferencedAssemblies) +278
System.Data.Metadata.Edm.ObjectItemCollection.LoadFromAssembly(Assembly assembly) +61
System.Data.Metadata.Edm.MetadataWorkspace.LoadFromAssembly(Assembly assembly) +77
System.Web.DynamicData.ModelProviders.EFDataModelProvider..ctor(Object contextInstance, Func`1 contextFactory) +327
System.Web.DynamicData.ModelProviders.SchemaCreator.CreateDataModel(Object contextInstance, Func`1 contextFactory) +110
System.Web.DynamicData.MetaModel.RegisterContext(Func`1 contextFactory, ContextConfiguration configuration) +347
System.Web.DynamicData.MetaModel.RegisterContext(Type contextType, ContextConfiguration configuration) +79
AdventureWorksDDMVC.MvcApplication.RegisterRoutes(RouteCollection routes) in C:\Dev\AdventureWorksDDMVC\AdventureWorksDDMVC\Global.asax.cs:30
AdventureWorksDDMVC.MvcApplication.Application_Start() in C:\Dev\AdventureWorksDDMVC\AdventureWorksDDMVC\Global.asax.cs:36


If I removed the adapter and container created by the EFPocoClassGen utility the problem goes away. I tried debugging the DynamicData assembly to see what the key was which was causing the exception, but it seems like the version of System.Web.DynamicData on my system (3.5 Sp 1) and the debug symbols on the server don't match because stepping through most of the code the current line didn't match what the debugger was seeing.

Finally a collegue who had been using the DynamicData sample application (and wasn't having problems) compared his project with mine and pointed out that the sample project didn't have an edmx file, but was using the csdl, ssdl and msl from a compiled edmx. So we re-added the adapter and container classes from the POCO generator and deleted the designer.cs file generated by Visual Studio for the edmx. This fixed the problem. So then we went into the properties for the edmx and removed the value for the "CustomTool" property setting to prevent the designer.cs class from being regenerated.

I'd like to dig further and figure out exactly what was causing the duplicate key issue, but for now I just wanted to describe the issue and point out the solution/workaround.

Labels: , , ,