views:

95

answers:

3

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.

+2  A: 

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.

RPM1984
Thanks for your answer. Is it possible to have the Repository return an ICollection of an Interface, e.g. ICollection<IPerson> while using the EF?
Timo Kosig
Another question :-) : how much do you expose your Repositories to the remaining application? Is there some logic, e.g. IPersonProvider.GetPerson(string name) that is called by the application and ultimately hides the repository or does the application know about IRepository?
Timo Kosig
@Timo Kosig - Both good questions. :) 1) Yes, i think it would be possible - as long as you're returning ICollection and not IQueryable. For this, you'll need to use Pure POCO's (no code generation). Which i use. But the downside of this is you'll need to make all your POCO's implement an interface, purely to help out the repository. Not a good idea IMO. You should be abstracting away the persistence, not the fact that you are using POCO's. POCO's are simple and cool - no need to hide them. :)
RPM1984
2) Well im currently working on an ASP.NET MVC application. The Controllers only talk to a "Service Layer". the UI has no reference to the Repositories DLL. The Service talks to the Repository, where an instance is injected into the Service Layer ctor by DI (StructureMap). And yes - the repositories are implemented with generics. There is a root interface called `IRepository<T>` which defines Add, Find, Remove, etc. Then in my DI, i say "If someone requests `IRepository<Person>`, give them `GenericRepository<Person>`. THis is another generic class implementing the interface. Cool huh?
RPM1984
The end result is my controllers go: `personService.Find(p => p.Name == "Bob");` which calls the Service Layer (PersonService), then the Service layer goes: `repository.Find(predicate).SingleOrDefault();`. However, i like using a modified version of the specification pattern to define operations for searching (less maintenance, more flexibility/power, also why i use IQueryable). You don't have to do this of course. Depends on who is consuming your repository.
RPM1984
@RPM1984: Thank you very much for elaborating on my questions. You were able to already shed some light into the darkness over here :). Concerning 1) - the POCO's would have to implement those interfaces. Since in our SW architecture the UI does not reference the assembly which is responsible for storage directly but a common "hub" assembly which connects all subsystems together we can not work directly with POCO's. The "hub" over here is probably your service layer (and the interfaces are defined there).
Timo Kosig
@Timo Kosig - if your UI cannot reference the POCO's, then what return type will come back from the Repository? If your saying you want to return IPage (for example) from your Repo, where is that interface declared? In the common "hub" assembly? Slightly confused on your architecture. If your Repo was working with "Page" entities for example, and you wanted to return a collection of IPage, you couldn't just do db.Pages.ToList(). You would need to add it to a pre-declared `ICollection<IPage> list`. As i said, you lose deferred execution/IQueryable here.
RPM1984
A: 

You could make repositories which require your interfaces over the concrete objects, and using partial classes of the EF-generated classes implement those interfaces. Whether that is really worth the effort though, I'm not sure.

Isaac Abraham
A: 

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);
    }
}
Tom Brothers
I'm really scared by this code. What on earth are you doing?
RPM1984
@Tom: that looks like some proper black magic to me ;-). I'm not sure I understand it fully either. Does this.GetEntityType<T>() require some kind of mapping of interface to EntityType or is it just enough that our half of the partial entity class is set to implement that interface?
Timo Kosig
This Repository method tests to see if the type of T is an Interface. If it is, it will look up the actual EntityObject class by calling GetEntityType. The GetEntityType method checks to see if the type of T has been mapped to an EntityObject by using the TableMaps property (which is explained in the L2S example). Once the EntityObject is known, the Repository method is called again via reflection using the EntityObject type instead of the Interface. The results of this second Repository call are then cast to the type of the Interface.
Tom Brothers
Black magic is the best explanation of what is going on in this method… but I’ll see if I can demystify it a bit.
Tom Brothers
This Repository method tests to see if the type of T is an Interface. If it is, it will look up the actual EntityObject class by calling GetEntityType. The GetEntityType method checks to see if the type of T has been mapped to an EntityObject by using the TableMaps property (which is explained in the L2S example). Once the EntityObject is known, the Repository method is called again via reflection using the EntityObject type instead of the Interface. The results of this second Repository call are then cast to the type of the Interface.
Tom Brothers
I probably should add that this statement “return result as IQueryable<T>;” will only work in .Net4 thanks to the new Covariance feature.Admittedly, this logic seems a messy but it is all that I could do to get around the fact that EF doesn’t work with the Cast<> method.
Tom Brothers
Oh i kind of see what your doing here. What i did was use Pluralization (new feature of .NET4) to retrieve the entity set based on the entity name. I actually do this as well, but not didnt want to mention it because a) it's scary, and b) kind of out of scope for this question. I see what your doing now though, but you dont need to use reflection.
RPM1984