Tuesday, October 11, 2011

Extension Methods to Translate Interface Values

Today I'm going to talk about using two of my favorite concepts in C#:  interfaces and extension methods.

There are times when I'm using the Entities Framework with MVC.  For any writable objects in my MVC project, I really do not want to shoehorn them into an Entities object; EF will bark at you, there's a lot of complications, and the self-tracking stuff will actually get in your way.

However, at some point, my MVC model needs to allow me to get the data from an Entities data model object, and translate it to view model object, and vice-versa.  For example, let's say we have a database table of Employees:

create table Employees
(
EmployeeID int identity not null primary key,
GivenName varchar(35) not null,
FamilyName varchar(35) not null,
DepartmentID int not null
);

In EF, this will be an Employee object with four primitive properties (EmployeeID, GivenName, FamilyName, and DepartmentID) plus a navigation object property pointing to the Departments table called Department (assuming of course that DepartmentID is a foreign key and you are keeping foreign keys around... I like to) of type Department, another EF entity.

If we are making a model for MVC to edit Employee records, you don't want to use the Entity object; you cannot define any validation properties, the "glue" that holds an EF object is not translatable to the a serializable forms object (well, not strictly true, but it's a lot more work), and of course data state cannot be maintained very easily in a web session... you usually don't want it to anyway.

So we need to define a model object.  Let's do so now:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;


namespace MyMVCProject.Models.Employees
{
public class EditModel
{
public int EmployeeID { get; set; }


[Required]
[MaxLength(35)]
[Display(Name="Given Name")]
public string GivenName { get; set; }


[Required]
[MaxLength(35)]
[Display(Name="Family Name")]
public string FamilyName { get; set; }


[Required]
public int DepartmentID { get; set; }


public IEnumerable<Department> AvailableDepartments { get; set; }
}
}

There's a lot of overlap between the view model and the entity object; after all, they are referencing the same thing, but because they are meant to do different things they have to be separate.

But now, in the controller, every time we want to link the two objects, we need to go through a translation mechanism:

using System.Web.Mvc;
using MyMVCProject.Models.Employees;
using MyMVCProject.DataModel.Entities;
namespace MyMVCProject.Controllers
{
public class EmployeesController : Controller
{
protected EntitiesUnitOfWork UnitOfWork = new EntitiesUnitOfWork();
public ActionResult Edit(int id)
{
var entity = UnitOfWork.GetEmployee(id);
var model = new EditModel();
model.EmployeeID = entity.EmployeeID;
model.GivenName = entity.GivenName;
model.FamilyName = entity.FamilyName;
model.DepartmentID = entity.DepartmentID;
model.AvailableDepartments = UnitOfWork.GetAvailableDepartments();
return View(model);
}

[HttpPost]
public ActionResult Edit(EditModel model)
{
if (ModelState.IsValid)
{
var entity = UnitOfWork.GetEmployee(model.EmployeeID);
entity.GivenName = model.GivenName;
entity.FamilyName = model.FamilyName;
entity.DepartmentID = model.DepartmentID;
UnitOfWork.Save();
return RedirectToAction("Index");
}
else
{
model.AvailableDepartments = UnitOfWork.GetAvailableDepartments();
return View(model);
}
}
}
}

...and that's just for one view model.  There's likely to be a CreateModel, and maybe a DetailModel as well that the translation would have to be coded for.  That's a lot of repeated code.

So how do you get around having to do that all the time?  With interfaces!

In our data model namespace, we can define an IEmployee interface like so:

namespace MyMVCProject.DataModel.Entities
{
public interface IEmployee
{
int EmployeeID { get; set; }
int DepartmentID { get; set; }
string GivenName { get; set; }
string FamilyName { get; set; }
}
}

...then, make a public partial class Employee in the data model namespace that implements the interface:

namespace MyMVCProject.DataModel.Entities
{
public partial class Employee : IEmployee
{
//nothing needed here; the T4 generated code has already implemented the interface; 
//we are simply defining the class as IEmployee.
}
}

Now, what I like to do at this point (and this is simply a personal preference) is, in the same file where the interface is defined, create a partial static class with an extension method for the translation.  Note that the key properties will need to be handled with care; you don't want to change a key property unless it has not been set.  EF will bark at you, and rightfully so.

namespace MyMVCProject.DataModel.Entities
{
public interface IEmployee
{
int EmployeeID { get; set; }
int DepartmentID { get; set; }
string GivenName { get; set; }
string FamilyName { get; set; }
}

public static partial class Translation
{
public static T Translate<T>(this T current, IEmployee source) where T : IEmployee
{
if (current.EmployeeID == 0)
{
current.EmployeeID = source.EmployeeID;
}
current.DepartmentID = source.DepartmentID;
current.FamilyName = source.FamilyName;
current.GivenName = source.GivenName;
return current;
}
}
}

After this, go back to the MVC view model, and add the reference to the IEmployee interface and the data model namespace.  No other work should be necessary if you kept the property names and data types the same.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using MyMVCProject.DataModel.Entities;
namespace MyMVCProject.Models.Employees
{
public class EditModel : IEmployee
{
public int EmployeeID { get; set; }

[Required]
[MaxLength(35)]
[Display(Name="Given Name")]
public string GivenName { get; set; }

[Required]
[MaxLength(35)]
[Display(Name="Family Name")]
public string FamilyName { get; set; }

[Required]
public int DepartmentID { get; set; }

public IEnumerable<Department> AvailableDepartments { get; set; }
}
}

Now we can use the method extensions in our controller with impunity:

using System.Web.Mvc;
using MyMVCProject.Models.Employees;
using MyMVCProject.DataModel.Entities;
namespace MyMVCProject.Controllers
{
public class EmployeesController : Controller
{
protected EntitiesUnitOfWork UnitOfWork = new EntitiesUnitOfWork();
public ActionResult Edit(int id)
{
var entity = UnitOfWork.GetEmployee(id);
var model = new EditModel()
.Translate(entity);
model.AvailableDepartments = UnitOfWork.GetAvailableDepartments();
return View(model);
}

[HttpPost]
public ActionResult Edit(EditModel model)
{
if (ModelState.IsValid)
{
var entity = UnitOfWork.GetEmployee(model.EmployeeID)
.Translate(model);
UnitOfWork.Save();
return RedirectToAction("Index");
}
else
{
model.AvailableDepartments = UnitOfWork.GetAvailableDepartments();
return View(model);
}
}
}
}

While this does not seem to save a lot of code in this example, keep in mind, this is a very simple example; few models actually contain so few properties, and we have changed the translation between the view model object(s) and the data model object to exactly one place for everything.  When you add additional models (like a CreateModel, where you need different validation logic), instead of doubling your text, it's just one line that will predictably behave exactly as all the other calls to this extension method.


















No comments:

Post a Comment