Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programming against interfaces & Entity Framework 4.0

I'm trying to figure out if it would be possible to stick to the mantra "Program against an interface, not an implementation." while using the Entity Framework 4.0.

While I found a page explaining how to stick to aforementioned rule while using Linq-to-SQL (look here) I'm very keen to know if it would be possible to do that with the Entity Framework (also using Linq).

This is how it is usually used:

var query = from pages in dataContext.Pages where pages.IsPublished select pages;

foreach (Page page in query)
{
    // do something with page...
    var routeQuery = from routes in page.Route where route.IsValid select routes;

    foreach (Route route in routeQuery)
    {
        // do something with route
    }
}

But I'd like to use it like this:

var query = from pages in dataContext.Pages where pages.IsPublished select pages;

foreach (IPage page in query)
{
    // do something with page...
    var routeQuery = from routes in page.Route where route.IsValid select routes;

    foreach (IRoute route in routeQuery)
    {
        // do something with route
    }
}

Essentially I would like to be able to pass the DataContext of the Entity Framework out of the assembly/subsystem where it is instanciated by using an interface. All information which is provided by the data context should come in the form of an interface, not an actual class.

I would like to keep the actual classes implementing the Entities internal to the assembly and only expose the interfaces which they implement.

Is this possible with the Entity Framework? If not, is there any other O/R mapper which can be used in this way?

If this is not a good way how to further de-couple the DB from the actual application I'd be keen to hear suggestions from you.

like image 681
Timo Kosig Avatar asked Oct 14 '10 08:10

Timo Kosig


2 Answers

Well a better solution (in my opinion), is to do the following:

Create Repositories for your Entity Data Model, exposing either ICollection<T> or IQueryable<T>

Use interfaces on your Repository:

public interface IRepository
{
   public ICollection<Person> Find(string name); // tighter, more maintanability    
   public IQueryable<Person> Find(); // full power! but be careful when lazy loading

Because of the interfaces, you can swap-in-out mocks, other ORM's:

public class MockRepo : IRepository
{ 
   public List<Person> persons; // mimics entity set
}

There is only so much you can abstract away.

If your worried about using ObjectSet (which is tied to EF), use POCO's.

Look at some of my other questions for more info (as we're building this architecture right now).

Also, think about using Dependency Injection. Here, you can get the repository out of the business of managing the ObjectContext - you can inject the Repository a Unit of Work (which is a wrapper for ObjectContext - so multiple repositories, or aggregate roots, can handle the same context).

In our solution, nothing touches Entity Framework (or any persistence logic) apart from the Repositories, which are in a seperate assembly.

HTH.

like image 132
RPM1984 Avatar answered Oct 20 '22 01:10

RPM1984


You can follow most of the logic in the L2S example that you mentioned to implement an Interface based EF Repository class. The one major difference is the Repository method. It is a bit different because EF doesn’t work with Cast<> when casting from one IQueryable to another. I used reflection to get passed this issue.

Here is an example of the Repository method re-written for an EF Repository class:

public IQueryable<T> Repository<T>()
    where T : class
{
    if (typeof(T).IsInterface)
    {
        // if T is an interface then get the actual EntityObject Type by calling GetEntityType
        // so that entityType can be used to call back to this method 
        Type entityType = this.GetEntityType<T>();
        MethodInfo mi = this.GetType().GetMethod("Repository");
        // set the T in Repository<T> to be the entity type
        Type[] genericTypes = new Type[] { entityType };
        mi = mi.MakeGenericMethod(genericTypes);
        // call Repository<T>
        object result = mi.Invoke(this, new object[0]);
        return result as IQueryable<T>;
    }
    else
    {
        return this._context.CreateQuery<T>(this.GetEntitySetName<T>());
    }
}

private Type GetEntityType<T>()
{
    if (this.TableMaps.ContainsKey(typeof(T)))
    {
        return this.TableMaps[typeof(T)];
    }
    else
    {
        return typeof(T);
    }
}
like image 37
Tom Brothers Avatar answered Oct 19 '22 23:10

Tom Brothers