【问题标题】:Most efficient way to convert a object to another (Model to ViewModel)将对象转换为另一个对象的最有效方法(模型到视图模型)
【发布时间】:2016-10-17 19:58:22
【问题描述】:

假设我有一个包含 20 个字段的模型,并且在我的索引页面中,我想列出存储在我的数据库中的所有模型。 在索引页面中,我没有列出模型的所有字段,而只列出了3个字段。

所以,我做了两个类:

class CompleteModel {
    public int Id { get; set; }
    public string Field01 { get; set; }
    public string Field02 { get; set; }
    public string Field03 { get; set; }
    public string Field04 { get; set; }
    public string Field05 { get; set; }
    ...
    public string Field20 { get; set; }
}

现在,在我的控制器中,我可以使用:

await _context.CompleteModel.ToListAsync();

但我觉得这似乎不是正确的做法,因为我得到了所有字段并且只使用了 3 个字段。

所以,我编写了这段代码:

class ViewModel {
    public string Field02 { get; set; }
    public string Field04 { get; set; }
    public string Field08 { get; set; }
}

var result = _context.CompleteModel.Select(
    x => new {
        x.Field02,
        x.Field04,
        x.Field08
    }).ToListAsync();

var listResults = new List<IndexViewModel>();
if (result != null)
{
    listResults.AddRange(results.Select(x => new IndexViewModel
    {
        Field02 = x.Field02,
        Field04 = x.Field04,
        Field08 = x.Field08
    }));
}

我认为这样做需要很多代码。 首先,我选择了我想要的所有字段,然后将所有内容复制到另一个对象。

有一种“更直接”的方式来做同样的事情吗?

喜欢:

_context.CompleteModel.Select(x => new IndexViewModel { Field02, Field04, Field08 });

【问题讨论】:

  • 您是否获取所有 20fields 值?还是只选择了 2,4 和 8?
  • efficient?代码、性能(计算与传输)、内存?
  • 可以跳过中间的.Select(),使用List&lt;IndexViewModel&gt; model = _context.CompleteModel.Select(x =&gt; new IndexViewModel(){ Field02 = x.Field02, Field04 = x.Field04, ..... }.ToList();
  • @MatthewWhited 内存。 ps:我的例子比实际应用要简单得多。
  • 感谢@StephenMuecke。对于本示例,在没有第三方软件包(如 AutoMapper)的情况下尝试并运行良好。

标签: c# asp.net-mvc model-view-controller asp.net-core


【解决方案1】:

您可以使用AutoMapper 来减少样板,这样您就不会手动复制字段值。

如果您包含 AutoMapper NuGet 包,那么您需要在启动时在某个地方为您的类配置以下内容:

Mapper.Initialize(cfg => cfg.CreateMap<CompleteModel, ViewModel>());

然后您可以执行以下操作:

var results = await _context.CompleteModel.ToListAsync();
var viewModelResults = results.Select(Mapper.Map<ViewModel>).ToList();

该软件包有很多配置选项,因此请查看文档以了解它是否适​​合您的需求并确定最佳使用方式。

【讨论】:

  • 我也打算这样做,尽管我不建议使用 AutoMapper,因为它真的很慢。使用基于 MSIL 的较新对象映射器之一(TinyMapper、FastMapper 等),它们比 AutoMapper 快 10 倍。
  • 谢谢@SledgeHammer,我去看看!我认为重点是尽可能使用库来自动化样板代码,以便我们开发人员可以专注于有趣的代码:)
  • @SledgeHammer:FastMapper 似乎没有得到积极维护,2 年没有更新,不太可能很快获得 .NET Core 支持。没有打开的问题也表明没有多少人可以使用它,并且它仍然可能充满错误。在 99% 的使用 AutoMapper 的情况下,性能都不是问题。您永远不会在 1 或 1000 万行的结果集上使用它,您甚至可能也不想在这里使用 EF。充其量你返回 100-200 个结果并使用分页
  • @Tseng - 在高容量环境中,您可以轻松地每天甚至每小时映射 100 万多个对象。在我的测试中,使用 AutoMapper 的简单类对于 AutoMapper 需要 13 SECONDS 而对于 IL 映射器则需要 60 MILLISECONDS。 IL Mapper 通常是 1:1 与手工编写的代码。是的,我使用了缓存的 AutoMapper :),只是 AutoMapper 非常慢。它很强大,但很慢。但是,是的,每小时 13 秒的开销并不是什么大问题 :),但最好更快。
  • @SledgeHammer:我会稍微慢一点,但积极维护库,而不是一个快速的不支持/维护/积极开发的库(考虑到整个请求的持续时间,这只多 0.1% )。从长远来看,使用未维护的产品将花费您更多的成本,而不是您从一点性能中节省的成本。过早的微优化是万恶之源
【解决方案2】:

在我看来,这是过度抽象和分层的弱点之一。 VM 包含在使用环境(屏幕、流程等)中对您的应用程序有价值的数据。数据模型包含所有可能相关的可存储数据。在某些时候,您需要匹配两者。

使用 EF Projection 仅将您需要的数据从数据库中提取到投影数据模型类中(使用 EF POCO 层来定义查询,但不存储结果数据)。

如果存在简单映射,请使用 Automapper 或类似工具将投影类映射到您的 VM。但是,除非您只是编写 CRUD 屏幕,否则逐个字段映射的简单字段价值不大;您通过 EF 从数据存储中获取的数据是原始的,可能是关系形式。您的 VM 所需的数据可能不太适合该表单(同样,除非您正在执行简单的 CRUD 表单),因此您需要通过编码数据存储和视图之间的关系来增加一些价值型号。

我认为专注于代码行数会导致错误的方法。我认为您可以查看该代码并询问“它是否增加了任何价值”。如果您可以将任务委托给 Automapper,那就太好了;但是,如果您可以始终将数据模型的任务委派给 VM 数据复制,那么您的 VM 除了添加一些验证注释之外并没有真正发挥作用。

【讨论】:

  • @RafaelMarques 是的——这就是我要表达的意思。使用 Automapper 作为“解决方案”的人没有抓住重点。如果您可以自动映射它,它不会增加任何价值,而且您的 VM 可能与您的数据模型几乎没有什么不同 - 在这种情况下,您所做的只是复制 Microsoft Access。
  • @PhillipH 极其错误。 AutoMapper 不只是将一个类映射到同一个类。它可以映射到“轻量级”版本、不同的命名约定、删除某些属性(即 WCF)、FLATTEN、PROJECT、以更复杂的方式组合属性、转换类型等。这一切都是自动完成的。如果您认为它所做的只是 1:1 映射,那是您对它的幼稚理解。
  • AutoMapper 不会减少对象或从数据库中提取的数据。如果您想减少这些事情,您需要使用适当的 ViewModel 并按照 Philip 的建议使用 EF 投影。
  • @SledgeHammer - 再说一次,我不是说“不要使用映射工具”,而是说映射工具只是“它在锡上所说的” - 它映射。它可能会引入一些不错的新映射语义,以至于它(又)另一种查询语言位于所有其他查询协议之上,但它所做的一切都是以一种描述性的方式将数据从一组类拉到另一个类。根据我的经验,许多 VM 需要以非声明性方式从其数据模型中转换数据,这意味着编写一些程序代码。映射器在某些情况下非常有用,但不能涵盖所有情况。
猜你喜欢
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 2013-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-22
相关资源
最近更新 更多