将ViewModel投影回Model的最佳方法

问题描述 投票:6回答:2

考虑使用ViewModel:

public class ViewModel
{
    public int id {get;set;}
    public int a {get;set;}
    public int b {get;set;}
}

和这样的原始模型:

public class Model
{
    public int id {get;set;}
    public int a {get;set;}
    public int b {get;set;}
    public int c {get;set;}
    public virtual Object d {get;set;}
}

每次我获得视图模型时,我必须将所有ViewModel属性逐个放入Model中。就像是 :

var model = Db.Models.Find(viewModel.Id);
model.a = viewModel.a;
model.b = viewModel.b;
Db.SaveChanges();

这总会导致很多问题。我甚至有时会忘记提及一些属性然后发生灾难!我在寻找类似的东西:

Mapper.Map(model, viewModel);

BTW:我只使用automapper将Model转换为ViewModel,但反之亦然,我总是面临错误。

c# asp.net-mvc asp.net-mvc-4 mvvm automapper
2个回答
8
投票

总体而言,这可能不是您正在寻找的答案,但这是AutoMapper作者的引用:

我不能为我的生活理解为什么我要将DTO直接转回模型对象。

我相信从ViewModel映射到Entity的最佳方法是不使用AutoMapper。 AutoMapper是一个很好的工具,用于映射对象而不使用静态以外的任何其他类。否则,每个添加的服务都会使代码变得更加混乱和混乱,并且在某些时候您将无法跟踪导致您的字段更新,集合更新等的原因。

经常面临的具体问题:

  1. 需要非静态类来为您的实体进行映射 您可能需要使用DbContext来加载和引用实体,您可能还需要其他类 - 一些将图像上传到您的文件存储的工具,一些用于密码的哈希/盐等非静态类...等等必须以某种方式将它传递给automapper,在AutoMapper配置文件中注入或创建,这两种做法都非常麻烦。
  2. 可能需要在同一ViewModel(Dto) - >实体对上进行多次映射 对于相同的viewmodel-entity对,您可能需要不同的映射,具体取决于此实体是否为聚合,或者不是+基于您是否需要引用此实体或引用和更新。总的来说这是可以解决的,但在代码中会产生很多不需要的噪音,甚至更难维护。
  3. 非常脏的代码很难维护。 这个是关于原语(字符串,整数等)和手动映射引用,转换值等的自动映射。对于automapper,代码看起来很奇怪,你必须为属性定义映射(或者如果你喜欢隐式的自动化映射 - 当与ORM配对时也具有破坏性)并使用AfterMap,BeforeMap,Conventions,ConstructUsing等来映射其他属性,这使得更复杂的东西。
  4. 复杂映射 当你必须进行复杂的映射时,比如从2个以上的源类映射到1个目标类,你将不得不进一步复杂化,可能需要调用以下代码: var target = new Target(); Mapper.Map(source1, target); Mapper.Map(source2, target); //etc.. 该代码会导致错误,因为您无法将source1和source2映射到一起,并且映射可能取决于将源类映射到目标的顺序。如果您忘记进行1次映射,或者如果您的地图在1个属性上存在冲突的映射,并且相互覆盖,我就不会说话。

这些问题可能看起来很小,但是在我使用自动化库来将ViewModel / Dto映射到Entity的几个项目中,它比从未使用过更加痛苦。

以下是一些链接:


2
投票

为此,我们编写了一个简单的映射器。它按名称映射并忽略虚拟属性(因此它适用于实体框架)。如果要忽略某些属性,请添加PropertyCopyIgnoreAttribute。

用法:

PropertyCopy.Copy<ViewModel, Model>(vm, dbmodel);
PropertyCopy.Copy<Model, ViewModel>(dbmodel, vm);

码:

public static class PropertyCopy
{
    public static void Copy<TDest, TSource>(TDest destination, TSource source)
        where TSource : class
        where TDest : class
    {
        var destProperties = destination.GetType().GetProperties()
            .Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
        var sourceProperties = source.GetType().GetProperties()
            .Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
        var copyProperties = sourceProperties.Join(destProperties, x => x.Name, y => y.Name, (x, y) => x);
        foreach (var sourceProperty in copyProperties)
        {
            var prop = destProperties.FirstOrDefault(x => x.Name == sourceProperty.Name);
            prop.SetValue(destination, sourceProperty.GetValue(source));
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.