【问题标题】:Repository Pattern, POCO, and Business Entities存储库模式、POCO 和业务实体
【发布时间】:2010-09-15 17:08:43
【问题描述】:

我知道这里已经有很多关于存储库模式的线程,但不知何故我觉得我的问题有点不同。可能是因为昨天第一次听说POCO这个词。

我的问题是——通常,我在我的业务实体中添加和保存方法。假设我正在编写一个问答网站,并且我有以下实体:问题、答案和 cmets。如果我想使用存储库模式,我基本上只需要保留我的业务实体中的属性(例如,Question),并将我的操作移动到存储库类(例如,QuestionRepository),对吗?如果这是真的,POCO 是否意味着只有属性的业务实体?

我使用的是 Entity Framework 4.0,它在后面的 edmx 代码中创建了我的实体。如果我想使用存储库模式,则无需编写我自己的业务实体(问题、答案等),因为它们已经由 EF 生成,对吧?我只需要做 CRUD 的存储库吗?对于这个示例,我将拥有三个存储库,每个实体一个?

【问题讨论】:

  • 赞成取消不明原因的反对
  • 我对“不知何故我觉得我的问题有点不同”的有目的的重复解释投了反对票。这是一个三重欺骗。 “我需要 ViewModels 吗?”、“每个实体一个存储库?”、“EF 中的 POCO 是什么”之前都已得到解答,并且可以通过搜索轻松获得。

标签: c# .net asp.net-mvc entity-framework entity-framework-4


【解决方案1】:

先观察一下Asp.net MVC项目模板

我必须说对 Visual Studio 的 Asp.net MVC 项目模板有一个小小的误解。这就是 Model 文件夹。不了解 MVC 模式的人会自动将其与数据模型相关联,而不是 MVC 应用程序/表示模型。这对于我们不区分两者但不区分其他任何东西的简单应用程序来说很好。

让我们继续我的回答

当我编写业务级应用程序时,我将我的解决方案分为 4 个项目(至少):

  • 表示层 - Asp.net MVC 应用程序,但我删除了模型文件夹并将我的所有视图作为强类型视图,以尽可能避免 魔术字符串
  • 服务层 - 业务逻辑流程
  • 数据层 - 数据模型,即。 EF4访问此模型的存储库
  • 对象层 - 这个项目实际上有用于层间通信的 POCO 和各个层使用的任何接口(想想 IoC)

我的请求过程通常看起来很干净,并且是这样工作的:

  1. 发出请求时,我的 Asp.net MVC 控制器操作会验证数据(POCO 对象),在调用服务之前执行表示层所需的任何操作。
  2. 调用服务来执行业务流程逻辑所需的任何操作,并且通常调用存储库来处理数据。
  3. 存储库操作数据模型中的数据,然后根据将返回到服务层的结果创建 POCO。
  4. 服务层接收 POCO 会在需要时执行额外的逻辑并将它们返回给表示。
  5. Presentation(控制器)决定显示哪个视图,并为该特定视图提供模型并返回它。当然,它也可以是任何其他结果,而不是视图。

在 Objects 项目中使用单独的 MVC 模型类的优点(由于循环项目引用,您不能将它们放在 Model 文件夹中)是我可以拥有演示优化类。或者说得更好:我有以业务流程为中心的界面,而不是以数据为中心。

让我们用一个例子来解释一下:以用户注册视图为例。它不能强类型化到数据模型的User 实体。为什么?因为它有两个密码输入。所以我可以有一个名为UserRegistration 的应用程序/表示模型类,即使数据模型中没有类似的东西。与数据模型的User 实体相比,它的验证工作方式完全不同。如果我在没有强类型的情况下完成用户注册,我必须使用每个字段的所有参数来使我的控制器操作。这些不会被自动验证,这意味着我可以拥有更大的错误表面。有人可能会急于编写代码,但却忘记了验证的某些方面。

在服务器返回强类型的强类型视图是摆脱通常由用户发现的各种晦涩错误的最安全方法,尤其是如果您不对项目进行任何有条不紊的测试(大约在 75 -90% 的几率)。

【讨论】:

  • 我对此赞不绝口!我已经说过很多次了,我通常会让 cmets 关注 YAGNI 或者我疯了,这告诉我大多数人只是在编写不会从这些想法中受益的简单 CRUD 应用程序。这是我在这里不再活跃的主要原因。大多数人都会忽略“按照 MSFT 所说的去做”以外的任何建议。
  • @Ryan:非常感谢。尽管我还没有看到这里的开发人员盲目地遵循某些公司的观点。至少不会让我远离这个网站。
  • 我正在尝试对此进行可视化...在此模型中,带有 repositories数据层 是否返回 POCO,因为数据层 references 对象层,从而可以构建它们?或者你有一个单独的 interfaces 层?
  • @lorddev: objects tier 定义了由各个层共享的所有类型:POCO 应用程序模型类、服务接口、存储库接口等。尤其是设计精美的服务和存储库您将能够为两者重用接口。也许每个都有一个单独的服务接口与存储库接口。但是您可能会涵盖每个都将消耗和输出 POCO 的 CRUD 场景。 回答您的问题:是的,所有层都严格通过 POCO 进行通信。包括从数据创建应用层 POCO 实例的存储库...
  • @RobertKoritnik 很棒的回答 +1。只是几个小问题。您的对象层基本上包含从您的控制器传递到您的视图的(视图)模型,对吗?另外,我假设我们将域模型(服务层返回到表示层的实体)映射到控制器内部的视图模型?
【解决方案2】:

前段时间我和 OP 处于一个非常相似的地方,所以我将在了解存储库模式后用一些代码来扩展 Roberts 的答案,说明我如何构建我的 asp.net mvc 应用程序。

所以你的项目是 QandA

您将拥有一个名为QandA.data 的类库项目,您将在这里创建您的 edmx 文件和所有实体框架类。然后你有一个像这样的每个实体的存储库:

public interface IRepository<T>
{
    T Save(T entity);
    void Delete(T entity);
    IQueryable<T> GetAll();
    T GetById(int id);
}

然后您可以拥有一个工厂或使用依赖注入来获取实际的存储库。所以:

class QuestionRepo : IRepository<Question>
{
 //call xxxEntites and get/save/delete yourentities here.
}
static class RepositoryFactory
{
 public static IRepository<Question> GetQuestionRepo()
 {
  return new QuestionRepo();
 }
}

然后在你的调用代码中(在你的 asp.net 项目中)你有

IRepository<Question> qRepo = RepositoryFactory.GetQuestionRepo();
Question q =  qRepo.GetById(1);

现在执行上述操作的好处是,您的调用代码不知道实体是如何通过的,因此您可以创建一个模拟存储库来测试您的应用。

static class RepositoryFactory
{
 public static IRepository<Question> GetQuestionRepo()
 {
  return new FakeQuestionRepo();
  //create your own fake repo with some fixed fake data.
 }
}

现在,如果您将代码扔到假的或真实的存储库中,您调用的代码根本不会改变。

此外,罗伯特在他的问题中谈到的是 ViewModel。因此,您不会制作 Question 类型的强类型页面。所以你有

class QuestionForm
{
 public string Title
 public string QuestionContent
}

您的页面将是 QuestionForm 类型,但在您的创建控制器中,您将从问题表单中获取数据,将其填写到您的问题实体中,然后通过存储库发送。

[HttpPost]
public ActionResult Create(QuestionForm quesfrm)
{
 IRepository<Question> qRepo = RepositoryFactory.GetQuestionRepo();
 Question ques = new Question {
 AskedDate = DateTime.Now,
 Title = quesfrm.Title,
 Content = QuestionContent
 }
  qRepo.Save(ques);
}

Robert 提到了您这样做的原因之一,还有其他一些原因,您可以阅读更多关于 SO 上的视图模型的信息。另请查看nerddinner 的代码

您可能希望看到这些 SO 问题:
Should repositories implement IQueryable<T>?
Repository pattern: One repository class for each entity?

希望对你有所帮助。

【讨论】:

    【解决方案3】:

    您的 POCO 对象仍然具有用于操作的方法,但这些操作将与实体的业务关注点有关,而不是持久性关注点 (CRUD)。如果您的实体上没有业务操作,那么是的,它们将只是属性。

    如果您正在生成 pocos,则无需从头开始编写它们,但您可能希望使用部分类扩展它们以用于业务操作和非持久性或计算属性。

    您可以有一个存储库类或每个实体都有一个存储库。

    【讨论】:

    • 我认为这是不正确的 - 如果您使用存储库模式,您的业务对象不包含操作 - 这一切都委托给存储库。因此,您的 User 对象将具有 Name、Surname 等,但您的 UserRepository 对象将具有 GetUsers 和 SaveUser 方法
    • @Jaco Pretorius:问题对象可能具有 AddAnswer、Close、SelectAnswer 等操作,这将是业务问题。问题对象没有保存、获取、更新或删除操作,因为这些是持久性问题,并且是存储库的责任。对您的业务对象没有任何操作会导致贫乏的域模型en.wikipedia.org/wiki/Anemic_Domain_Model,这通常被认为是一种反模式。
    • 我同意 PhilB 的观点,我们只使用存储库来处理持久性问题,实体可以根据需要拥有自己的操作来处理其业务逻辑。
    猜你喜欢
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    • 2010-12-11
    • 2011-08-06
    • 2011-04-02
    相关资源
    最近更新 更多