2

所以可以说我有一个对象

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 嵌入视图模型中。

是否有一种干净的编程方式可以在对象之间复制相同名称和类型的属性,或者我是否被手工操作卡住了?

4

3 回答 3

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");
}

例如,假设您有一个包含 Username、Password 和 IsAdmin 等属性的 User 域模型,并且您有一个允许用户更改其用户名和密码但绝对不更改 IsAdmin 属性的表单。因此,您将拥有一个包含 Username 和 Password 属性的视图模型,该属性绑定到视图中的 html 表单,并且使用此技术您将只更新这 2 个属性,而不会更改 IsAdmin 属性。

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

Mapper.CreateMap<MyItem, MyItemViewModel>();

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

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

所以不要再等了,在你的 NuGet 控制台中输入以下命令并享受表演:

Install-Package AutoMapper
于 2012-08-15T13:57:13.690 回答
2

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

这是它的例子

完整:通过反射的简单属性映射器:停止手动复制对象的每个属性!

 /// <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);
        }
      }
    }
于 2012-08-15T13:59:34.147 回答
1

如果您想从头开始做事,一些反射代码可以在这里为您提供帮助。性能不是很好,但肯定很容易设置和维护。检查此SO 答案中提供的方法。

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

MSDN 文章在这里

获取公共属性的示例:

PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
于 2012-08-15T13:59:48.523 回答