【问题标题】:Design question POCO objects / DAL access设计问题 POCO 对象/DAL 访问
【发布时间】:2011-05-07 09:25:39
【问题描述】:

我想实现典型的三层架构。我目前的做法如下

  • DAL - 使用 EF 4.0 和我的每个实体的存储库。通过接口访问
  • 我正在考虑使用 POCO 对象。我的第一个问题是我应该把那些 文件?在所有其他项目引用的程序集中?
  • BLL - 我如何将数据从 DAL 获取到 BLL,然后最终到 GUI 如果我在那里有一大堆经理类,比如 BLL 中的 CustomerManager,这是一个好方法吗?这些类将访问 BLL 中的相应存储库,然后将对象传递给 GUI

或者您认为将存储库放在 BLL 中并直接从我的 say buttoneventhandler 中访问它们更好吗?

希望你能给黑暗带来一些光明

【问题讨论】:

  • 如果我们的回答有帮助,请将其标记为已接受

标签: .net entity-framework repository-pattern poco


【解决方案1】:

我们在 DAL 中有存储库。 BLL 通过接口引用存储库 - 因此存储库与 DAL 绑定,但与 BLL 分离。我不知道为什么存储库不能直接在 BLL 中。我们将它们放在 DAL 中,因为我们没有在其中添加任何逻辑。然后我们在 BLL 中有“管理器”,它包装存储库并处理特定于实体的逻辑。

FWIW 我们实际上有一个通用的Repository(Of IEntity) 并根据需要使用统一来实例化适当的存储库 - 它非常紧凑且非常优雅。我们所有的 POCO 实体都实现了 IEntity,其中包含我们所有实体共有的 IdCreatedDate 等。当您需要一般处理任何类型的实体时,这会带来一些其他好处 - CreatedDate 在调用 CreateInstance() 时由存储库设置,ModifiedDate 在提交状态为 @ 的实体时由上下文本身设置987654327@

我们将实体保存在单独的项目中 - DAL 需要能够引用它们,BLL 也是如此。您不希望它们在 DAL 中,因为交换 DAL 会导致问题。你不能把它们放在 BLL 中,否则你会得到一个循环引用。实体的配置可以存在于 DAL 中,因为它是特定于数据源的。

我们尝试坚持 BLL 接收原语并返回实体。请注意不要将实体在 UI 中保留太久,尤其是在 Web 应用程序中,因为当您将实体返回到 BLL 进行处理时(即跨存储在会话或类似中的请求),您可能在 DAL 下具有不同的上下文可能会导致从上下文中附加/分离实体的各种有趣的事情,并且会失去一些好处,例如更改跟踪。

希望对您有所帮助,但如果您需要任何澄清,请告诉我

【讨论】:

  • 其实不,我们再次使用统一/依赖注入来返回对单个实例的引用。这是因为管理器依赖于 repos,而 repos 又依赖于非静态的上下文。您可能可以解决这个问题,但我们没有。
  • 还有一个小问题。您在命名方面有什么建议。您是否使用类似 CustomerManager 或 RPM1984 编写的 CustomerService 之类的东西?
  • 我们倾向于使用“Manager”来表示仅与 1 个实体(例如 CustomerManager)交互的东西。我们将“服务”用于更复杂的多实体功能 - 例如与 UserManager、RoleManager 等交互的 MembershipService - 这再次允许我们使我们的管理器几乎相同的副本。管理器处理他们自己实体的实例化、更新等。在我们的 Db 中,对实体调用 Delete 只会将记录状态更改为“非活动”。管理器确定是否允许当前用户访问已删除的实体...继续v
  • 如果没有,它会将“AND RecordStatus Inactive”附加到所有查询,因为它们被传递到存储库。这允许服务简单地调用 Manager.Get() 并且只检索适当的记录。
  • 哦,顺便说一句 - 您的设置与我们的非常相似。我们还使用IRepository<T> 并通过StructureMap 注入GenericRepository<T>。唯一不同的是我们的实体没有从 IEntity 继承,因为那样它们就不是真正的 POCO。它们与持久性本身无关,但“更新日期时间”(我对 POCO 的一个问题)的事实是一个数据库问题,因此我的观点是失去整个 POCO。我还没有弄清楚如何更新日期时间。我有三个选项 - 1)触发器(yuck),2)savechanges delegate(yuck),或 3)BLL(可能)
【解决方案2】:

这是我们的设置:

Company.Project.Domain.Model      (POCOs)
Company.Project.Business.Services (BLL)
Company.Project.Data.Repositories (Repository)
Company.Project.Web               (Presentation)
  • POCO 在它们自己的程序集中。 什么都不引用,并且被所有人引用
  • BLL 对存储库执行查询,应用业务规则和水合。 返回 ICollection<T>T参考资料库(通过 IRepository<T> 通用接口)和 POCO
  • Repository 是一组实现IRepository<T> 的通用类,以提供针对底层存储的基本持久性。查找、添加、删除等。返回IQueryable引用 POCO 的被 BLL 引用
  • 演示文稿是 UI。 参考 POCO 和 BLL

最终的结果是类似堆栈的方法,并且因为基本上所有内容都是通过接口(和 DI 注册表),所以灵活性非常大。

我们有通过 DI 注入测试项目的模拟存储库,以及通过 DI 注入 BLL 的实体框架存储库。

来自 UI 的示例流程 -> DB:

// "Company.Project.Web.ProductsController.cs"
public class ProductsController : BaseController
{
   private IProductService _productService;

   public ProductsController(IProductService productService)
   {
      this._productService = productService; // DI makes me happy :)
   }

   public ActionResult GetOrdersForProduct(Product product)
   {
      var orders = _productService.GetOrders(product);
      return View(orders);
   }
}

// "Company.Project.Business.Services.ProductService.cs"
public class ProductService : IProductService
{
   private IRepository<Product> _productRepository;

   public ProductService (IRepository<Product> productRepository)
   {
      this._productRepository = productRepository; // DI makes me happy :)
   }

   public ICollection<Orders> GetOrdersForProduct(Product product)
   {
      return _productRepository
                .Find()
                .ForProduct(product) // IQueryable<Product> pipe/extension filter
                .ToList();
   }
}

// "Company.Project.Data.Repositories.GenericRepository.cs
public class GenericRepository<T> : IRepository<T> where T : class
{
   private IObjectSet<T> _objectSet;

   public GenericRepository(IUnitOfWork unitOfWork)
   {
      CurrentContext = unitOfWork as SqlServerUnitOfWork;
   }

   public IQueryable<T> Find()
   {
      return CurrentContext.GetEntitySet<T>();
   }

   protected SqlServerUnitOfWork CurrentContext { get; private set; }

   protected IObjectSet<T> CurrentEntitySet
   {
      // some plularization smarts in "SqlServerUnitofWork.cs", to dynamically pull
      // back entity set based on T.
      get { return _objectSet ?? (_objectSet = CurrentContext.GetEntitySet<T>()); }
   }
}

如你所见,我是 DI 粉丝。

【讨论】:

  • 只是一个性能建议 - 而不是返回 ICollection 尝试返回 IQueryable - 如果 UI 只需要 s 子集(例如对于分页)
  • @Basiclife - 阅读我关于存储库的部分。我正是这样做的。 IMO 他们应该被推迟到 BLL,而不是进一步到 UI,否则你可能会得到意想不到的结果。分页很简单,只需将 pagenumber/pagesize 传递给 BLL 中的方法,该方法会跳过/接受 repo 中的 iqueryable。
  • 顺便说一下,这不是延迟加载,而是延迟执行。注意不要混淆两者。我在 EF 中禁用延迟加载,并使用 .Include 急切加载 BLL 所需的实体。
  • @RPM1984,您是手写 POCO 还是使用 EF4 POCO 生成器生成它们?
  • 两者兼而有之。 :) 如果从现有数据库生成模型,我使用生成器,然后将代码复制到我单独程序集中的类中。这样做很好,因为 EF POCO 非常基于约定。如果您在手动编码时拼错了某个属性,您将收到一个(不太明显的)错误。
【解决方案3】:

您可以通过将存储库和 POCO 保存在同一个项目中来保持这一点非常简单。这基本上就是您的数据域模型。您的 POCO 是公开的,您的存储库接口也是如此。您应该将具体存储库保留在此项目内部。

您的 BLL 可以是经典的 Facade 或 Service Locator。该项目将处理您的数据并应用任何相关的业务规则,然后再将其交给 UI。这还将负责在将数据发送到 DAL 之前验证来自 UI 的数据。

【讨论】:

  • 有趣的方法。您的实体配置在哪里?即属性、索引的 SQL 数据类型?
  • 我不确定我是否正确理解了您的问题。您的数据模型 (.edmx) 将抽象出对 SQL 的所有访问。
  • 嗯,好吧 - 我们使用 EF4 代码优先,所以我们的实体是我们手动创建的类,我们手动制作我们的配置(键、FK 等),这意味着我们可以保留特定于 SQL 的东西类似于绑定到 DAL 的数据类型,但具有通用的 POCO 实体。
猜你喜欢
  • 2010-10-09
  • 2011-05-27
  • 2011-12-04
  • 2010-10-11
  • 2011-08-25
  • 1970-01-01
  • 2010-09-15
  • 2010-12-27
  • 2010-10-04
相关资源
最近更新 更多