存储库模式,POCO和业务实体

时间:2010-09-15 17:08:43

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

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

我的问题是 - 通常,我在我的业务实体中添加和保存方法。假设我正在写一个Q / A网站,我有以下实体:问题,答案和评论。如果我想使用存储库模式,我基本上只需要保留业务实体中的属性(例如,问题),并将我的操作移动到存储库类(例如,QuestionRepository),对吧?如果这是真的,那么POCO是指具有属性的商业实体吗?

我正在使用Entity Framework 4.0,后者在edmx代码中创建了我的实体。如果我想使用存储库模式,那么就不需要编写我自己的业务实体(问题,答案等),因为它们已经由EF生成了,对吧?我只需要存储库来做CRUD吗?我将为这个例子提供三个存储库,每个实体一个?

3 个答案:

答案 0 :(得分:33)

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

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

让我们继续回答

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

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

我的请求流程通常看起来非常干净并且以这种方式工作:

  1. 当发出请求时,我的Asp.net MVC控制器操作会验证数据(POCO对象),在调用服务之前执行表示层所需的任何操作。
  2. 调用服务来执行业务流程逻辑所需的任何操作,并且通常调用存储库来执行数据处理。
  3. 存储库操纵数据模型中的数据,然后从将返回服务层的结果创建POCO。
  4. 服务层接收POCO会根据需要执行其他逻辑并将其返回演示文稿。
  5. 演示文稿(控制器)决定显示哪个视图并为该特定视图提供模型并将其返回。当然,也可以是任何其他结果,而不是视图。
  6. 在Objects项目中使用单独的MVC模型类(由于循环项目引用而无法将它们放在 Model 文件夹中)的优点是我可以使用表示优化的类。或者更好地说:我有以业务流程为中心的界面而不是以数据为中心。

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

    在服务器上返回强类型的强类型视图是摆脱用户通常发现的各种模糊错误的最安全的方法,特别是如果您不对项目进行任何有条不紊的测试(介于75之间) -90%的几率)。

答案 1 :(得分:8)

我之前和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。因此,您不会创建类型为问题的强类型页面。你有

class QuestionForm
{
 public string Title
 public string QuestionContent
}

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

[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);
}
罗伯特提到了为什么要这样做的原因之一,还有其他一些原因,你可以在SO上阅读更多有关视图模型的内容。另请查看nerddinner

的代码

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

我希望它有所帮助。

答案 2 :(得分:5)

您的POCO对象仍然会有操作方法,但这些操作与实体的业务问题有关,而不是持久性问题(CRUD)。如果您的企业没有业务运营,那么他们将只是财产。

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

您可以为每个实体设置一个存储库类或存储库。