If you decide to use ASP.NET MVC with the Entity Framework, you will quickly come to see that the perfect one entity per view scenario that most of the examples show is not realistic. Enter the need for ViewModels. The ViewModels are specially shaped classes that act as an intermediary between the 1...n entities that will eventually comprise a single view (or partial view).

The challenge is when it comes to repopulating the original 1...n entities that comprised the ViewModel upon the user making an insert or an update. In our perfect one entity to one view scenario, MVC model binding takes care of all the work repackaging our entity in the controller, and it is a matter of a few lines of code to add that object to the EF context and save the changes. Code in this scenario would be as straight forward as the following:

When using ViewModels, a convenient option to help reduce any manual mapping back to entities is to use an open source product named ValueInjecter. You can download the binaries from NuGet located here.

Value Injecter allows us to easily map properties from a source object to a destination object without what would be normally required to rehydrate the multiple entities. The main method we will use is the exposed InjectFrom() which will allow us to inject the values from the source to destination object. Once complete, we can then persist our entities back to the data store using EF with only a minimal amount of additional coding.

To begin let's take a look at the following ViewModel class I created based on (3) different tables from the AdventureWorks database: Contact, Employee, and EmployeeDepartmentHistory. The idea here is that we need to add a new employee record here that spans these (3) tables. The focus here is not on the schema from the AdventureWorks database but rather just to display how Value Injecter can be used to persist data to these (3) entities. As you can see below the ViewModel class has fields from each of the tables; however for brevities sake I have kept the class small. In reality there are quite a few more required fields that would have to be defined.

Next I have an EmployeeController class with a Create() method that takes in my ViewModel type as its parameter. I am going to inject the values from the model bound ViewModel object data into the individual underlying entities and then save the changes.

[HttpPost]
public ActionResult Create(EmployeeViewModel employeeViewModel)
{
if (ModelState.IsValid)
{
using (var context = new AdventureWorksEntities())
{
var contact = new Contact();
//Inject the values into a Contact entity using ValueInjecter
contact.InjectFrom(employeeViewModel);
//Do NOT use the title submitted by the client,
//but rather override with a default value
contact.Title = string.Empty;
var employee = new Employee();
//Inject the values into an Employee entity using ValueInjecter
employee.InjectFrom(employeeViewModel);
var employeeDepartmentHistory = new EmployeeDepartmentHistory();
//Inject the values into a EmployeeDepartmentHistory entity using ValueInjecter
employeeDepartmentHistory.InjectFrom(employeeViewModel);
employee.EmployeeDepartmentHistories.Add(employeeDepartmentHistory);
contact.Employees.Add(employee);
context.Contacts.AddObject(contact);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(employeeViewModel);
}
}

Note the explicit assignment of the Title property on the Contact object above. In the Adventureworks database, both the Contact and Employee tables have a Title field. My Employee view had an input for the client intended to fill out the Employee's title and not the Contact's title. Value Injecter by default will not discriminate and simply hydrate the destination object based on the matched property name (as designed so this is OK). Just make sure to intervene where needed if you run into a situation with identically named properties on objects.

That's it! There is actually a lot more functionality to ValueInjecter than what was shown here so I encourage you to look at the Documentation section on its CodePlex page. However even just this example can be scaled nicely to large legacy or existing databases where the 1:1 view per entity scenario rarely occurs. In this case consider using ValueInjecter to save on a lot of additional coding when using ViewModels in ASP.NET MVC.

Related Postings:

0
comments:

Post a Comment

About Me

I am a Magenic Associate Principal Consultant that is an advocate of web and Microsoft .NET technologies both professionally and personally. I enjoy the challenge and creativity behind software engineering, and hope during this process to extract some of my thoughts and ideas in order to give back to others in the community through public speaking and here on this blog.