-
-
Save PaulStovell/2380031 to your computer and use it in GitHub Desktop.
| @model UserViewModel | |
| @using (Html.BeginForm()) | |
| { | |
| <div> | |
| @Html.HiddenFor(m => m.Id) | |
| @Html.TextBoxFor(m => m.FirstName) | |
| @Html.TextBoxFor(m => m.LastName) | |
| @Html.DropDownListFor(m => m.CountryId, Model.Countries) | |
| <input type="submit" /> | |
| </div> | |
| } |
| interface IModelBuilder<TViewModel, TEntity> | |
| { | |
| TViewModel CreateFrom(TEntity entity); | |
| TViewModel Rebuild(TViewModel model); | |
| } |
| interface IModelCommand<TInput> | |
| { | |
| void Execute(TInput model); | |
| } |
| class UserEditCommand : IModelCommand<UserEditModel> | |
| { | |
| readonly ISession session; | |
| public UserEditCommand(ISession session) | |
| { | |
| this.session = session; | |
| } | |
| public void Execute(UserEditModel model) | |
| { | |
| var user = string.IsNullOrEmpty(model.Id) ? new User() : session.Find<User>(model.Id); | |
| session.Store(user); | |
| user.FirstName = model.FirstName; | |
| user.LastName = model.LastName; | |
| user.Country = session.Find<Country>(model.CountryId); | |
| session.SaveChanges(); | |
| // Auditing and other interesting things can happen here | |
| } | |
| } |
| class UserController : Controller | |
| { | |
| UserViewModelBuilder builder = new UserViewModelBuilder(); | |
| UserEditCommand saveCommand = new UserEditCommand(); | |
| public ActionResult Edit(string id) | |
| { | |
| var user = session.Find<User>(id) ?? new User(); | |
| return View(model, builder.CreateFrom(user)); | |
| } | |
| [HttpPost] | |
| public ActionResult Edit(UserViewModel model) | |
| { | |
| if (!ModelState.IsValid) | |
| { | |
| return View(builder.Rebuild(model); | |
| } | |
| saveCommand.Execute(model); | |
| return RedirectToAction("Index"); | |
| } | |
| } |
| class UserEditModel | |
| { | |
| public string Id { get; set; } | |
| public string FirstName { get; set; } | |
| public string LastName { get; set; } | |
| public string CountryId { get; set; } | |
| } | |
| class UserViewModel : UserEditModel | |
| { | |
| public ICollection<SelectListItem> Countries { get; set; } | |
| } |
| class UserViewModelBuilder : IModelBuilder<UserViewModel, User> | |
| { | |
| readonly ISession session; | |
| public UserViewModelBuilder(ISession session) | |
| { | |
| this.session = session; | |
| } | |
| public UserViewModel CreateFrom(User user) | |
| { | |
| var model = new UserViewModel(); | |
| model.Id = user.FirstName; | |
| model.FirstName = user.FirstName; | |
| model.LastName = user.LastName; | |
| model.Country = user.Country.Id; | |
| model.Countries = GetCountries(); | |
| return model; | |
| } | |
| public UserViewModel Rebuild(UserViewModel model) | |
| { | |
| model.Countries = GetCountries(); | |
| return model; | |
| } | |
| ICollection<SelectListItem> GetCountries() | |
| { | |
| var countries = session.FindAll<Country>(); | |
| return countries.Select(c => new SelectListItem { Value = c.Id, Text = c.Name }).ToList(); | |
| } | |
| } |
Thanks Jay, you're right that the naming convention could be clearer - I've renamed them.
I like the idea of being able to raise exceptions from the command. Do you try/catch within the controller, or do you have an action filter that handles it?
Hi Paul,
Thanks for this ! :)
As I'm learning MVC and EF right now, I have a question regarding the ISession interface.
class UserViewModelBuilder : IModelBuilder<UserViewModel, User>
{
readonly ISession session;
public UserViewModelBuilder(ISession session)
{
this.session = session;
}
Can you post the EF layer code and injection into the controller ?
How is the constructor fired passing the session ?
UserViewModelBuilder(ISession session)
Thanks, LA Guy
Hi Paul,
I know it is old but still active :)
When you use PRG pattern and copy ModelState between Redirect->Get then you don't needed Rebuild method anymore. :)
Regards
Darek
Looking good to me - we tend to postfix models with either "ViewModel" or "PostModel" (eg. EditCustomerViewModel and EditCustomerPostModel) which helps my brain grasp which one I'm working with. Our commands also tend to have a parameterless Execute() method - we simply set properties on the command, but passing the PostModel works well too.
Most of our GET actions follow this pattern:
[HttpGet]
public ActionResult Edit(int id) {
var builder = new EditCustomerViewModelBuilder();
var model = builder.Build(id);
return View(model);
}
Our POST actions looks pretty similar to yours - the commands can throw CommandFailureExceptions etc, which allow you to add them back into the collection of ModelState errors.