How to avoid the need for references to the Entity Framework at my service level?

When trying to create a data access level for a new project, I came across what I can only imagine as an OOP / design / generalization problem (using EF 4.3 to access the database).

Basically, I wanted to achieve two things with this data layer:

  • The various context objects that I have in my project must have the same connection string.
  • An abstract repository class with a common implementation.

For some reason, I cannot compile my solution without reference to EntityFramework at the service level. What I'm looking for is a way to fix this. Here is what I have:

//Project/Namespace BusinessLogicLayer.DomainClasses //POCO classes mapped on Entity Framework. //Project/Namespace DataAccessLayer.Base //Base classes and interfaces for all data access layer, such as: public abstract class BaseContext<TContext> : DbContext where TContext : DbContext { //To allow multiple contexts sharing the same connection string protected BaseContext(): base("name=MyConnectionString") {} } //Generic interface for a read-only repository public interface IReadOnlyRepository<T> : IDisposable where T : class //Generic interface for a read/write repository public interface IRepository<T> : IReadOnlyRepository<T> where T : class //Basic implementation for a read-only repository public abstract class BaseReadOnlyRepository<C, T> : IReadOnlyRepository<T> where T : class where C : BaseContext<C>, new() { } //Basic implementation for a read/write repository public abstract class BaseRepository<C, T> : IRepository<T> where T : class where C : BaseContext<C>, new() { } //Project DataAccessLayer.AccountContext/ Namespace DataAccessLayer //Context class: public class AccountContext : BaseContext<AccountContext> {} //With this, I can have simple repositories: public class UserRepository : BaseRepository<AccountContext, User> { //All implementation comes from the base abstract class, unless I need to change it (override methods) } 

I have a service level between data access and an application (Windows Forms). Since I have a common repository, a good and logical idea is to have shared services. After all, it is very similar to the structure of the repository:

 //Project/Namespace BusinessLogicLayer.Services //Service layer supposed to reference only the repository project and not Entity Framework. //Generic interface for a read-only service working with a read-only repository public interface IReadOnlyService<T> where T : class {} //Generic interface for a read/write service working with a read/write repository public interface IService<T> : IReadOnlyService<T> where T : class //Base implementation for a read-only service public abstract class BaseReadOnlyService<T, R> : IReadOnlyService<T> where T : class where R : IReadOnlyRepository<T>, new() { } //Base implementation for a read/write service public abstract class BaseService<T, R> : IService<T> where T : class where R : IRepository<T>, new() { } //Concrete sample service public class UserService : BaseService<User, UserRepository> { //As with the repository I can change the default behavior of implementation overriding methods } 

With this setting, the only way to compile is to reference the Entity Framework in a service level project. How can I avoid the need to reference the Entity Framework?

At this point, I’m even ready to throw it all away and rebuild everything, but this is the only way to make it work according to my needs (DbContext uses connection strings, a common repository to avoid code replication).

Appreciate any help. Thanks.

- Edit - Including here are some additional steps that I took 3 hours after I posted the question -

To understand this, I began to create a sample project with the same code above plus some implementations in order to maximize the results in the original project.

I created a project of domain classes, an entire project of a level of a basic data level, and then a context project. I noticed that I need to associate the Entity Framework with the context project, even if the context class cannot be obtained directly from DbContext. Instead, it comes from an abstract class that comes from DbContext. This is normal, though, since my context will have DbSets and any other implementation related to DbContext.

Next is the repository project. You must reference all the other three (domain, core data layer, and context). My repository has no code. All funcionality rests with the ancestor. I am trying to compile a repository project, and VS requires me to abstract the Entity Framework. I wonder if this is really a library implementation issue. If this is confirmed, it will be a surprise. The Entity Framework library is present at the output of other projects. Why should I refer here? What makes VS demand this?

In any case, for testing purposes, I added a link. In the end, I'm inside the data layer. I can live with it. Transition to the service level. For simplicity, I put all the service classes in the same project.

One possible drawback is that one of the limitations for abstract service classes is the repository interface. This requires me to add a reference to the basic data layer at my service level. Perhaps there is already something here that I can do that allows me to use only the link to the repository. I have no choice but to refer to the basic level of data.

Finally, my specific service was created, and VS gives me the following error message: The type "System.Data.Entity.DbContext" is defined in an assembly that is not referenced. You must add a reference to the assembly "EntityFramework, Version = 4.3.1.0, Culture = neutral, PublicKeyToken = b77a5c561934e089".

So, in the end, the only way to continue is to bundle the Entity Framework at the service level. And at some point, when creating the Windows Forms application, I will also have to refer to the Entity Framework.

What should I do to avoid these links? What improvements can I have in this structure?

I know that my application, of course, should not know that the Entity Framework is involved anywhere on other levels. Also not a service level. Services will only use storage. Repositories can even provide fake test data.

In case anyone is interested, I downloaded the project that I created when writing. This is a 1.17Mb zip file without any binaries (other than the Entity Framework 4.3.1 DLL that I got through Nuget). Link: http://www.mediafire.com/?b45zkedy2j7eocc .

Again, thanks for the help.

+8
generics c # oop entity-framework
source share
1 answer

Instead of the abstract BaseContext in the BusinessLogicLayer , declare the interface. Then implement it at your data access level.

 public interface IDataContext : IDisposable { int SaveChanges(); } //Generic interface for a read-only repository public interface IReadOnlyRepository<T> : IDisposable where T : class //Generic interface for a read/write repository public interface IRepository<T> : IReadOnlyRepository<T> where T : class //Basic implementation for a read-only repository public abstract class BaseReadOnlyRepository<C, T> : IReadOnlyRepository<T> where T : class where C : IDataContext { } //Basic implementation for a read/write repository public abstract class BaseRepository<C, T> : IRepository<T> where T : class where C : IDataContext { } public interfaces IAccountContext : IDataContext { //other methods } 

Then in the data access layer

 public abstract class BaseContext : DbContext, IDataContext { //To allow multiple contexts sharing the same connection string protected BaseContext(): base("name=MyConnectionString") {} } public class AccountContext : BaseContext, IAccountContext {} //With this, I can have simple repositories: public class UserRepository : BaseRepository<AccountContext, User> { //All implementation comes from the base abstract class, unless I need to change it (override methods) } 

You can use DI / Ioc to insert Context and Repository into services instead of creating a context instance inside the repository.

This decoupling will eliminate the need for a reference to the EF assembly at the business logic level, but remember that your domain objects are not completely independent of EF. For example, navigation properties, relationship fixes will not work outside the EF context. So you really hide the addiction!

+8
source share

All Articles