7

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.

4 Answers 4

6

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.

Sign up to request clarification or add additional context in comments.

7 Comments

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?
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 - 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. :)
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?
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.
|
1

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);
    }
}

7 Comments

@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?
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.
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.
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.
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.
|
0

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.

Comments

0

I have achieved this with EF 5.0. The solution is too complex to post. I can give a very high level description.

The base class for the repository looks like...

public class GenericRepository<TEntityInterface, TEntity> 
    where TDataInterface : TEntityInterface
    where TEntity : class, IBaseEntityInterface, new()

A subclass of this repository would look like...

public class EmployeeRepository<TEntity> : GenericRepository<IEmployeeEntity, TEntity>, IEmployeeRepository
where TEntity : class, IEmployeeEntity, new()

I use T4 templates to customize generation of entities to inherit entity interfaces, alternatively you could create partial classes for each EF entity to inherit interfaces. I've used code gen scripts to generate interfaces for the entities that correlate with the properties on the entities. All entities inherit IBaseEntityInterface.

At some point in your code (in my case using an INject injection framework), you marry the EF entity with the repository as so....

Bind<IEmployeeRepository>().To<EmployeeRepository<EmployeeEntity>();

Where EmployeeEntity is generated by the entity framework.

There is an issue with this approach, entity framework doesn't like LINQ joins between entity interfaces, doing so can result in an error, depending on how the query is structured.

You have to do the queries in the repository against TEntity. You can use navigation properties on entity interfaces (and on TEntity in the repository) to effectively do joins A-OK.

However sometimes you'll want to do joins without navigation properties, the work around is to expose methods on the repositories that return primitive IQueryable objects for use in other repositories e.g a query of IDs such as IQueryable or codes IQueryable, creating these within a repository for another repository to include them in a query.

I think on the whole, in a large code base, the benefits of using entity interfaces instead of directly referencing EF entity classes outweighs the problems you'll encounter using EF with entity interfaces. Apart from loose coupling, robustness to change etc, you can put a lot of code in the base repository and program against TEntity with the properties on your base entity interface.

e.g A method to get a collection of Entity interfaces matching a predicate with a number of properties pre-loaded

    public IList<TEntityInterface> Where(Expression<Func<TEntityInterface, bool>> predicate, params string[] includedProperties)
    {
        DbQuery<TEntity> query = Context.Set<TEntity>();

        foreach (string prop in includedProperties)
            query = query.Include(prop);

        return query.Where(predicate).ToList<TEntityInterface>();
    }

As far as I'm concerned referencing Entity Framework entities within your code is basically heavily coupling the code to the EDMX and hence to a specific database schema. If you have need to support multiple database schemas with the same code base, Entity Framework out of the box leaves you pretty high and dry.

Hopefully with future version of EF these issues will be resolved and we can all be good little programmers and use interfaces instead of EF classes without these sorts of tricks.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.