复制不同类型对象的最简单方法

时间:2012-08-15 13:54:35

标签: c# asp.net-mvc

所以我想说我有一个对象

class MyItem
{
     public string Name {get; set;}
     public string Description {get; set;}
}

我想创建一个ViewModel

class MyItemViewModel
{
     public string Name {get; set;}
     public string Description {get; set;}
     public string Username {get; set;}
}

我想在控制器上获取MyItem类型的对象并自动填充ViewModel。使用MyItem中包含的值(即,如果它具有名为Name的属性,则自动填充它)。

我要避免的是Model.Name = Item.Name行的列表。 MyItemViewModel也将具有不同的属性和显示值,因此我不能简单地在视图模型中嵌入MyItem。

是否有一种干净的程序化方法可以在对象之间复制相同名称和类型的属性,或者我是不是手动操作了?

3 个答案:

答案 0 :(得分:5)

您可以使用AutoMapper执行此任务。我在所有项目中使用它来映射我的域模型和视图模型。

您只需在Application_Start中定义映射:

Mapper.CreateMap<MyItem, MyItemViewModel>();

然后执行映射:

public ActionResult Index()
{
    MyItem item = ... fetch your domain model from a repository
    MyItemViewModel vm = Mapper.Map<MyItem, MyItemViewModel>(item);
    return View(vm);
}

您可以编写一个自定义操作过滤器来覆盖OnActionExecuted方法,并使用相应的视图模型替换传递给视图的模型:

[AutoMap(typeof(MyItem), typeof(MyItemViewModel))]
public ActionResult Index()
{
    MyItem item = ... fetch your domain model from a repository
    return View(item);
}

这使您的控制器操作非常简单。

AutoMapper还有另一个非常有用的方法,当你想要更新某些东西时,它可以在你的POST动作中使用:

[HttpPost]
public ActionResult Edit(MyItemViewModel vm)
{
    // Get the domain model that we want to update
    MyItem item = Repository.GetItem(vm.Id);

    // Merge the properties of the domain model from the view model =>
    // update only those that were present in the view model
    Mapper.Map<MyItemViewModel, MyItem>(vm, item);

    // At this stage the item instance contains update properties
    // for those that were present in the view model and all other
    // stay untouched. Now we could persist the changes
    Repository.Update(item);

    return RedirectToAction("Success");
}

想象一下,例如,您有一个包含用户名,密码和IsAdmin等属性的用户域模型,并且您有一个允许用户更改其用户名和密码但绝对不更改IsAdmin属性的表单。因此,您将拥有一个视图模型,其中包含绑定到视图中html表单的用户名和密码属性,并且使用此技术,您将只更新这两个属性,保持IsAdmin属性不变。

AutoMapper也适用于集合。一旦定义了简单类型之间的映射:

Mapper.CreateMap<MyItem, MyItemViewModel>();

在集合之间进行映射时,您不需要做任何特殊的事情:

IEnumerable<MyItem> items = ...
IEnumerable<MyItemViewModel> vms = Mapper.Map<IEnumerable<MyItem>, IEnumerable<MyItemViewModel>>(items);

所以不要再等了,在NuGet控制台中键入以下命令并欣赏节目:

Install-Package AutoMapper

答案 1 :(得分:2)

你可能需要反思才能做到这一点

这是它的例子

完整:Simple properties Mapper by reflection: Stop copying manually each property of your objects !

 /// <summary>
    /// Copies all the properties of the "from" object to this object if they exist.
    /// </summary>
    /// <param name="to">The object in which the properties are copied</param>
    /// <param name="from">The object which is used as a source</param>
    /// <param name="excludedProperties">Exclude these properties from the copy</param>
    public static void copyPropertiesFrom
    (this object to, object from, string[] excludedProperties)
    {
      Type targetType = to.GetType();
      Type sourceType = from.GetType();

      PropertyInfo[] sourceProps = sourceType.GetProperties();
      foreach (var propInfo in sourceProps)
      {
        //filter the properties
        if (excludedProperties != null
          && excludedProperties.Contains(propInfo.Name))
          continue;

        //Get the matching property from the target
        PropertyInfo toProp =
          (targetType == sourceType) ? propInfo : targetType.GetProperty(propInfo.Name);

        //If it exists and it's writeable
        if (toProp != null && toProp.CanWrite)
        {
          //Copy the value from the source to the target
          Object value = propInfo.GetValue(from, null);
          toProp.SetValue(to,value , null);
        }
      }
    }

答案 2 :(得分:1)

如果你想从头做事,一些反思代码可以帮助你。性能不是很高,但很容易设置和维护。检查此SO answer

中提供的方法

您可以使用“Type.GetProperties”获取目标对象的属性列表。

MSDN article here

获取公共属性的示例:

PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);