Stephen A. Fuqua (SAF) is a Bahá'í, software developer, and conservation and interfaith advocate in the DFW area of Texas.

Dependency Injection with Entity Framework

August 21, 2013

Problem: how do you use Entity Framework, in database-first mode, when following the dependency injection development patterns?

Answer: take advantage of partial classes and manually create the interface. The following notes are just a sketch without full details.

For this demonstration, I'll create a new Entity Framework "ADO.NET Data Model" with a couple of tables and two stored procedure calls. This is a quick-and-dirty demonstration, so I'm not trying to setup Entity Framework and my procedure calls in an ideal fashion; it will be "just enough" for the demo.

efDIP_1.png

Now open the auto-generated class file for the model. Right click and choose Refactor > Extract Interface. Select the operations that you want to include in the interface. Typically, I like to expose the entire table collection and the methods.

efDIP_2.png

This action just updated the auto-generated file; you will lose that change next time you update the model from the database. Here's where the partial class comes in. Copy the class declaration line (public partial class IbaUnitTestEntities: ObjectContext, ClassLibrary1.IIbaUnitTestEntities. Create a new file with the same name as the model class, and paste that line in. Remove the implementation of ObjectContext. Result:

partial class IbaUnitTestEntities: IIbaUnitTestEntities
{
}

Now back to the interface file. Change it to be public. We need one more method call in there, one that is in ObjectContext and therefore was omitted by Visual Studio in the refactor command: the SaveChanges command. And, we need the interface to implement IDisposable so that we can wrap the object in a using statement (or try/catch/finally).

public interface IIbaUnitTestEntities : IDisposable
{
    int SaveChanges();
    ObjectSet<Location> Locations { get; }
    ObjectSet<Person> People { get; }
    ObjectResult<Person> Person_Get(Guid? id, string openId, bool? all);
    ObjectResult<Person> Person_Get(Guid? id, string openId, bool? all, System.Data.Objects.MergeOption mergeOption);
    int Person_Save(Guid? id, string firstName, string lastName, string openId, string emailAddress, string phoneNumber, string address1, string address2, string city, string state, string zipCode, string country, bool? hasBeenTrained, bool? hasClipboard, short? personStatus, short? personRole);
}

Compile your code - it should be succesful, with no further edits. Now let's look at using an Abstract Factory class for dependency injection.

public interface IFactory
{
    IIbaUnitTestEntities CreateDatabaseConnection();
}

public class Factory : IFactory
{
    public IIbaUnitTestEntities CreateDatabaseConnection()
    {
        return new IbaUnitTestEntities();
    }
}

Now the main program needs to instantitate the factory and inject it into a dependent class. For this demonstration, I'm taking the simple approach rather than using a DI container like Ninject, Spring, or Unity.

public class Program
{
    public static void Main(string[] args)
    {
        var factory = new Factory();
        var demo = new Class1(factory);
        demo.DemonstrateSomeCalls();
    }
}

Finally, let's see the some code that uses the Entity Framework without having an explicit dependence on the code generated by EF. Take particular note that this code snippet shows the interface and not the concrete class generated by EF.

public class Class1
{
    private readonly IFactory factory;

    public Class1(IFactory inputFactory)
    {
        if (inputFactory == null)
        {
            throw new ArgumentNullException("inputFactory");
        }
        factory = inputFactory;
    }

    public void DemonstrateSomeCalls()
    {
        using (IIbaUnitTestEntities db = factory.CreateDatabaseConnection())
        {
            // Call a stored procedure
            var allPeople = db.Person_Get(null, "", true)
                              .ToList();

            // Update the address for the first person in the list. 
            // Bad code! Doesn't check for null
            var firstPerson = allPeople.FirstOrDefault();
            firstPerson.Address1 = "new address";
            db.SaveChanges();

            // Add a person
            db.People.AddObject(new Person() { FirstName = "Stephen" });
            db.SaveChanges();
        }
    }
}

No TrackBacks

TrackBack URL: http://www.safnet.com/fcgi-bin/mt/mt-tb.cgi/113

Leave a comment