Rod*_*ira 8 .net c# oop generics entity-framework
在尝试为新项目创建数据访问层时,我遇到了我只能想象的OOP/Design/Generics问题(使用EF 4.3访问数据库).
主要是我想用这个数据层实现两件事:
出于某种原因,我无法在服务层上引用EntityFramework的情况下编译我的解决方案.我正在寻找的是解决这个问题的方法.这就是我所拥有的:
//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)
}
Run Code Online (Sandbox Code Playgroud)
我在数据访问和应用程序(Windows窗体)之间有一个服务层.因为我有一个通用的存储库,所以拥有通用服务似乎是一个好的逻辑思路.最后,非常类似于存储库结构:
//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
}
Run Code Online (Sandbox Code Playgroud)
使用此设置,编译的唯一方法是在服务层项目上引用Entity Framework.如何避免在那里引用实体框架?
此时,我愿意全力以赴并重建所有内容,但这是我发现根据我的需求使其工作的唯一方法(DbContext共享连接字符串,通用存储库以避免代码复制).
感谢任何帮助.谢谢.
- 编辑 - 包括我在发布问题3小时后做的一些额外步骤 -
为了解决这个问题,我开始使用上面相同的代码创建一个示例项目,并加上一些实现,以尽可能地模仿原始项目的结果.
我创建了域类项目,整个基础数据层项目,然后是上下文项目.我注意到我需要在上下文项目中引用Entity Framework,即使上下文类不是直接从DbContext派生的.相反,它派生自一个派生自DbContext的抽象类.这是可以的,因为我的上下文将有DbSets和任何其他与DbContext相关的实现.
接下来是存储库项目.需要引用所有其他三个(域,基础数据层和上下文).我的存储库没有代码.所有的功能都在于祖先.我尝试编译存储库项目,VS要求我引用实体框架.我想知道这是否只是嵌入库的问题.如果这被证实,那将是一个惊喜.实体框架库存在于其他项目的输出中.我为什么还要在这里引用它?是什么让VS需要这个?
无论如何,出于测试目的,我添加了参考.毕竟,我在数据层内.我可以忍受这一点.继续前进到服务层.为简单起见,我将所有服务类放入同一个项目中.
一个可能的缺陷是服务抽象类的约束之一是存储库接口.这要求我在服务层上添加对基础数据层的引用.也许已经在这里我可以做的事情允许我只使用存储库引用.我别无选择,只能引用基础数据层.
最后,我的具体服务已创建,VS给我以下错误消息:类型'System.Data.Entity.DbContext'在未引用的程序集中定义.您必须添加对程序集'EntityFramework,Version = 4.3.1.0,Culture = neutral,PublicKeyToken = b77a5c561934e089'的引用.
因此,最后,继续前进的唯一方法是在服务层上引用Entity Framework.在某些时候,在构建Windows窗体应用程序时,我还必须引用实体框架.
我应该怎么做以避免这些引用?我对这种结构有什么改进?
我所知道的是,我的应用当然不必知道实体框架涉及其他层的任何地方.服务层也没有.服务只会使用存储库.存储库甚至可以为测试提供虚假数据.
如果有人有兴趣,我上传了我写的这个项目.这是一个1,17Mb的zip文件,没有任何二进制文件(除了我通过Nuget获得的Entity Framework 4.3.1 dll).链接:http://www.mediafire.com/?b45zkedy2j7eocc.
再次感谢您的帮助.
声明一个接口,而不是BaseContext在你的抽象BusinessLogicLayer.然后在您的数据访问层中实现它.
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
}
Run Code Online (Sandbox Code Playgroud)
然后在数据访问层
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)
}
Run Code Online (Sandbox Code Playgroud)
您可以使用DI/Ioc将Context和Repository注入服务,而不是实例化存储库中的上下文.
这种解耦将消除在业务逻辑层中引用EF程序集的需要,但请记住,您的域实体并不完全独立于EF.例如,导航属性,关系修正在EF上下文之外不起作用.所以在某种程度上你实际上隐藏了一个依赖!