10

在尝试为新项目创建数据访问层时,我遇到了我只能想象为 OOP/设计/泛型问题(使用 EF 4.3 访问数据库)。

主要是我想用这个数据层实现两件事:

  • 我在项目中拥有的不同 Context 对象应该共享相同的连接字符串。
  • 具有通用实现的抽象存储库类。

出于某种原因,如果不引用服务层上的 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)
}

我在数据访问和应用程序(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
}

使用此设置,编译的唯一方法是在服务层项目上引用实体框架。我怎样才能避免在那里引用实体框架?

在这一点上,我什至愿意把它全部扔掉并重建所有东西,但这是我发现根据我的需要使它工作的唯一方法(DbContext 共享连接字符串,通用存储库以避免复制代码)。

感谢任何帮助。谢谢。

--编辑--包括我在发布问题 3 小时后执行的一些额外步骤--

为了弄清楚这一点,我开始创建一个示例项目,其中包含与上面相同的代码以及一些实现,以尽可能地模仿原始项目的结果。

我创建了域类项目、整个基础数据层项目,然后是上下文项目。我注意到我需要在上下文项目中引用实体框架,即使上下文类不是直接从 DbContext 派生的。相反,它从派生自 DbContext 的抽象类派生。这没关系,因为我的上下文将包含 DbSet 和任何其他与 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 。

再次感谢您的帮助。

4

1 回答 1

9

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
}

然后在数据访问层

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

您可以使用 DI/Ioc 将上下文和存储库注入服务,而不是在存储库中实例化上下文。

这种解耦将消除在业务逻辑层中引用 EF 程序集的需要,但请记住,您的域实体并非完全独立于 EF。例如导航属性、关系修复将无法在 EF 上下文之外工作。所以在某种程度上你实际上是在隐藏一个依赖!

于 2012-11-08T03:45:27.570 回答