2

So lets say I have an object

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

I want to create a ViewModel

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

I would like to on the controller get an object of type MyItem and automatically populate the ViewModel. With the values contained within MyItem (ie if it has a property called Name, automatically fill it out).

What I am trying to avoid is a list of Model.Name = Item.Name rows. MyItemViewModel will also have different attributes and display values so I cannot simply embed MyItem within the View Model.

Is there a clean programmatical way of duplicating attributes of the same name and type between objects or am I stuck by doing it by hand?

John Mitchell
  • 9,653
  • 9
  • 57
  • 91
  • 5
    See AutoMapper: http://automapper.codeplex.com/ – Alvaro Rodriguez Aug 15 '12 at 13:55
  • I'm not following why you can't create a property of type MyItem in your model and add the specific custom Model fields (attributes / display values) as new properties on the Model that rely on the MyItem property? This would be a great solution if possible. – Zaid Masud Aug 15 '12 at 14:00

3 Answers3

5

You could use AutoMapper for this task. I am using it in all projects to map between my domain models and view models.

You simply define a mapping in your Application_Start:

Mapper.CreateMap<MyItem, MyItemViewModel>();

and then perform the mapping:

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

and you could write a custom action filter which overrides the OnActionExecuted method and substitues the model that was passed to the view with the corresponding view model:

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

This makes your controller actions pretty simple.

AutoMapper has another very useful method which could be used in your POST actions when you want to update something:

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

Imagine for example that you had a User domain model containing properties like Username, Password and IsAdmin and that you have a form allowing the user for changing his username and password but absolutely not changing the IsAdmin property. So you would have a view model containing the Username and Password properties bound to an html form in the view and using this technique you will only update those 2 properties, leaving the IsAdmin property untouched.

AutoMapper also works with collections. Once you have defined a mapping between the simple types:

Mapper.CreateMap<MyItem, MyItemViewModel>();

you don't need to do anything special when mapping between collections:

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

So wait no more, type the following command in your NuGet console and enjoy the show:

Install-Package AutoMapper
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
2

You might need reflection to do this

here is example of it

Full : 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);
        }
      }
    }
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
1

If you want to do things from scratch, some reflection code can help you here. Not very performant, but certainly easy to set up and maintain. Check the method provided in this SO answer.

You can get the list of properties on your target object using "Type.GetProperties".

MSDN article here

Example of getting public properties:

PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
Community
  • 1
  • 1
Jay S
  • 7,904
  • 2
  • 39
  • 52