使用具有存储库模式的View-Models

时间:2014-04-25 08:26:14

标签: c# entity-framework ef-code-first repository viewmodel

我在最近的项目中使用Domain driven N-layered application architectureEF code first,我在Repository图层中定义了Domain个合同。 使其他Repositories更简洁的基本合同:

public interface IRepository<TEntity, in TKey> where TEntity : class
{
   TEntity GetById(TKey id);
   void Create(TEntity entity);
   void Update(TEntity entity);
   void Delete(TEntity entity);
}

每个Repositories都有专门的Aggregation root,例如:

public interface IOrderRepository : IRepository<Order, int>
{
    IEnumerable<Order> FindAllOrders();
    IEnumerable<Order> Find(string text);
    //other methods that return Order aggregation root
}

如您所见,所有这些方法都依赖于Domain entities。 但在某些情况下,应用程序的UI需要一些不是Entity的数据,这些数据可能来自两个或更多肠炎的数据(View-Model s),在这些情况下,我在View-Model中定义Application layer,因为它们非常依赖于Application's的需求,而不是Domain

所以,我认为我有两种方法可以在View-Models中将数据显示为UI

  1. 保留专门的Repository仅取决于Entities,并在我要向用户展示时将Repositories的方法的结果映射到View-Models(在{{ 1}}通常)。
  2. 向我的专业Application Layer添加一些方法,将结果直接返回Repositories,然后在View-Models中使用这些返回的值,然后Application Layer(这些专门的{{ 1}}我称之为UI的合同,放入Repositories,与放入Readonly Repository Contract的其他Application Layer'合同不同。 enter image description here
  3. 假设,我的Repositories需要Domain,其中包含3或4个属性(来自3或4 UI)。 它的数据可以通过简单的投影生成,但在第一种情况下,由于我的方法无法访问View-Model,我必须有时获取所有3或4个表的所有字段,巨大的连接,然后将结果映射到Entities。 但是,在案例2中,我可以简单地使用投影并直接填充View-Models

    所以,从性能的角度来看,我认为案例2优于案例1.但我认为View-Models应该依赖于View-Model而不是Repository的设计点图。

    有没有更好的方法不会导致Entities图层依赖于View-Models,也没有达到性能?或者,对于阅读查询,Domain取决于Application layer?(案例2)

    是否可以接受

3 个答案:

答案 0 :(得分:21)

也许使用 command-query separation (在应用程序级别)可能会有所帮助。

您应该使您的存储库仅依赖于实体,并且只在您的存储库中保留普通检索方法 - 即 GetOrderById() - 以及create /当然更新/合并/删除)。想象一下,实体,存储库,域服务,用户界面命令,处理这些命令的应用程序服务(例如,处理Web应用程序中的POST请求的某个Web控制器等)代表您的写模型,应用程序的写入方

然后构建一个单独的读取模型,可以像你想的那样脏 - 把5个表的连接放在那里,从文件中读取宇宙中恒星数量的代码,将它相乘以A开头的书籍数量(在对亚马逊进行查询之后)并建立一个n维结构等等 - 你明白了:)但是,在阅读模型上,不要添加任何处理的代码修改你的实体。您可以从此读取模型中自由返回所需的任何视图模型,但可以从此处触发任何数据更改。

读写分离应该会降低程序的复杂性并使一切变得更易于管理。而且您可能也会发现它不会违反您在问题中提到的设计规则(希望如此)。

从性能的角度来看,使用读取模型,即将读取数据的代码与写入/更改<的代码分开编写/ strong>数据是你能得到的最好的:)这是因为你甚至可以在那里破坏一些SQL代码而不会在晚上睡不着觉 - 如果写得好,SQL查询将为你的应用程序提供相当大的速度提升。

Nota bene:我开玩笑说你可​​以编写读取方的代码 - 读取端代码应该像干净一样简单,当然是写入端代码: )

此外,如果需要,您可以删除通用存储库接口,因为它只会混淆您正在建模的域,并强制每个具体的存储库公开不必要的方法:)请参阅{ {3}}。例如,很可能Delete()方法永远不会用于 OrderRepository - 因为,或许永远不应该删除Orders(当然,它总是依赖于它)。当然,您可以将数据库行管理原语保存在单个模块中,并在具体存储库中重用这些原语,但不要将这些原语暴露给除了存储库的实现之外的任何其他原语 - 只是因为在其他任何地方都不需要它们,如果公开暴露,可能会使醉酒的程序员感到困惑。

最后,也许不考虑域层应用层数据层查看模型也是有益的图层过于严格。请阅读this根据实际意义/目的(或功能)打包您的软件模块比基于不自然打包它们要好一些,< em>难以理解的,难以解释为5岁的孩子标准,即将它们逐层打包

答案 1 :(得分:1)

好吧,对我来说,我会将ViewModel映射到Model对象并使用我的存储库中的那些来进行读/写,因为您可能知道有几种工具可以为此做,在我个人的情况下我使用{{ 3}}我发现很容易实现。

尽量保持Web层和Repository层之间的依赖关系尽可能分开,说repos应该只与模型对话,并且你的web层应该与你的视图模型对话。

一个选项可能是您可以在服务中使用DTO并自动化Web层中的这些对象(可能是一对一映射的情况),缺点是您最终可能会遇到很多样板代码和dtos和视图模型可能会感觉重复。

另一种选择是返回模型中的部分水合物体,并将这些物体作为DTO展示,并将这些物体映射到您的视图模型,这个解决方案可能有点模糊,但您可以进行所需的投影,只返回信息你需要的。

你可以摆脱视图模型并在你的web层中公开dtos,并将它们用作视图模型,减少代码,但更多耦合方法。

答案 2 :(得分:1)

我有点同意佩德罗在这里。使用应用服务层可能是有益的。如果您的目标是MVVM类型的实现,我建议创建一个Model类,负责保存使用服务层检索的数据。如果您的实体,DTO和模型一致地命名(因此您不必编写大量手动映射),那么使用automapper映射数据是一个非常好的主意。

根据我的经验,在viewmodels中使用您的实体/ poco来显示数据会导致大量的泥浆。不同的视图有不同的需求,所有这些都需要为实体添加更多属性。慢慢地使您的查询更复杂,更慢。

如果您的数据没有改变,通常您可能需要考虑引入(sql / database)视图,这些视图会将一些繁重的工作转移到数据库(高度优化的地方)。 EF可以很好地处理数据库视图。然后检索实体并将数据(从视图)映射到模型或DTO变得相当简单。