【问题标题】:Design patterns advice - separating ViewModels from domain models设计模式建议 - 将 ViewModel 与域模型分开
【发布时间】:2011-03-16 20:54:14
【问题描述】:

我需要将 MVC 项目中的 ViewModel 与位于单独库中的业务模型(数据访问层)分开。

假设我在单独的库中有数据库访问层类。我们的主要项目 (MVC) 对这些类一无所知,它们通过接口相互通信。使用 IOC 和使用 Ninject 解决依赖注入很容易,对吧?

现在 DataAccessLayer 包含名为 Car 的类,具有 Engine 和 Wheel 属性。在我的 MVC 项目中(它对 DataAccessLayer 及其类一无所知)我需要使用一些 Car 对象。所以我有另一个 Car 类(它只是一个纯 ViewModel),它具有相同的属性 - Engine 和 Wheel(当然在实际应用中,model 和 viewmodel 之间会有一些差异,为了简单起见我们忽略它)

IDataAccessLayer 接口有一个名为 IEnumerable GetAllCars() 的方法,它返回 DataAccessLayer.Car 对象的列表。 现在我需要创建 MVCProject.Car 集合,遍历 GetAllCars() 返回的 IEnumerable,在每次迭代中我需要创建一个新的 MVCProject.Car 对象,填充 Engine 和 Wheel 属性,最后将该对象添加到集合中。

所以:每次我必须创建几乎相同的结构并以某种方式在不同的地方管理它们。

这就是问题所在,明白了吗?或者不是?我觉得如果我不改变它,它最终会变得一团糟。不要重复自己违反原则的行为。请告诉我如何使它正确。使用我不知道的代理或原型或其他一些我很烂的设计模式。或者像 Ninject(我只知道如何将其用作 IOC 容器)或 Automapper 之类的工具,我可能会比设计模式更糟糕。

【问题讨论】:

  • 你不烂...至少你在考虑设计模式:-)
  • 请更改标题以表明您的问题。
  • @David:阿门那个兄弟!
  • David Archer 或“你不烂……你在考虑 设计模式”:-/
  • @marcind.. 我应该在最后加上问号吗? :)

标签: c# design-patterns architecture asp.net-mvc-3 dependency-injection


【解决方案1】:

我也看不出将 DAL 与 MVC 层分开的理由。如果您有兴趣,这里是我用于多个项目的布局,我发现它非常实用且灵活。

数据对象 只有属性的基本对象。还包括 DataObjects 使用的枚举。

数据访问 继承DataObjects并添加GetByPrimaryKey、GetByForeignKey、GetByUniqueIndex、GetAll等。还包含缓存层,您可以在其中找到StateCache、CountryCache等,以便快速访问常用的东西。 “GetBy”方法将尽可能利用缓存层。

逻辑 静态类,每个 DataObject\Access 类型一个。包括除 DataAccess 层中详述的简单提取之外的逻辑工作。

网络\前端 UI 与 DataAccess 和 Logic 层一起使用以获取和更新对象以及调用其他已定义的逻辑 API。

我使用自己定制的代码生成器来生成 98% 的代码(UI 层除外)。

【讨论】:

  • 嗯,这当然是一个公平的架构,但出于好奇,由于您正在生成自己的 DAL,您如何处理复杂的查询,例如所有生日在 1982 年 3 月 22 日之前的客户至少一份金额 > 100 美元的订单,按姓氏排序?
  • 本例中的 DAL 使用的是 LINQ-to-SQL,由于该层可从 UI 层访问,因此就像编写 LINQ 查询一样简单。当然,如果它是可重用的,那么它会进入逻辑层。
  • 啊——那真是太棒了——尤其是因为其中 98% 是生成的。凉爽的。 +1
  • 谢谢。最终它将可用于一般用途。如果我有时间!
  • 你写过博客吗?您可能希望将其调整为 EF4 - linq-to-sql 即将死去......
【解决方案2】:

听起来您的 MVC 库应该了解您的数据访问库,不是吗?

或者,如果您真的想将 MVC 和 DAL 库分开,您总是可以添加第三个库,其中包含对 MVC 和 DAL 的引用。然后它可以处理从一个库中检索汽车,并将它们转换为另一个。

但是,我不明白为什么您的控制器(或 ViewModel,根据您的描述)不应访问 DAL。您的汽车 ViewModel 将从 DAL 检索 Cars 的实例,然后从那里开始。只要接收汽车的方式是通过接口编码的,您就应该能够稍后将其存根用于单元测试。

编辑

所以看起来您认为您稍后会更改整个 DAL,并且您希望将其难度降到最低?如果是这种情况,您可以查看adapter pattern。您会将所有 DAL 对象传递给适配器,适配器将返回给您业务层中的对象。然后,如果您的 DAL 发生变化,您只需更新您的适配器。

【讨论】:

  • 仍在苦苦挣扎。我需要将 GetCars 方法更改为可能采用 ViewModel.Car 类型并返回 IEnumerable 集合的通用方法。但是数据应该从 AccessDataLayer.Car 类型映射
  • +1:更改数据访问层的能力是傻瓜的黄金,不会给您带来真正的好处。要了解原因,请阅读Abstracting the persistence medium isn't going to let you switch
【解决方案3】:

我找到了一种让它变得非常丑的方法:)

目标:调用者对DataAccessLayer的模型实体一无所知,通过接口获取所有数据。 如何在不手动映射 Model 和 Viewmodel 的情况下获取数据? 通过反思。

 public IEnumerable<T> GetCars<T>()
    {
        var lst = new List<T>();

        _data.GetTable<Cars>().ToList().ForEach(x =>
        {
            var newCar = Activator.CreateInstance<T>();
            typeof(T).GetProperty("Engine").SetValue(newCar, x.Engine, null);
            typeof(T).GetProperty("Wheel").SetValue(newCar, x.Wheel, null);
            lst.Add(newCar);
        });

        return lst;
    }

它有效。但这里最大的问题是它是否公平且可观的架构决策?在现实生活中的应用程序中,性能将如何受到影响?

【讨论】:

  • 你应该为这样的事情使用反射。如果您需要将一种类型映射到另一种类型,请使用适配器。这是一个极其脆弱的解决方案。
  • @Adam:第二个。每当您在引号中看到属性名称时,您都在做“错误”的事情。或者至少这可能不是最好的方法。此外,在作为应用程序核心的代码中使用反射也会导致性能问题。有关我的建议的更多信息,请参阅我的回答。
  • @Adam 当然这是异端,永远不应该使用:) 就像寻找非传统和丑陋解决方案的一个例子。哎呀,我真的很讨厌软件架构设计:)
  • 不,你不烂——你只是太努力了。只需稍微放慢一点,允许更多的依赖项(例如依赖于 DAL 的 MVC),就会出现一个非常清晰的解决方案。你做得很好。
  • 我支持 automapper.codeplex.com。小伙伴们,你们怎么看?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-10
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
相关资源
最近更新 更多