简体   繁体   中英

How to mutate editmodel/postmodel to domain model

In an ASP.NET MVC project we are using AutoMapper to map from domain model to viewmodel - and sometimes also flattening a hierarchy while doing so. This works like a charm and makes the rendering logic of our views very lean and simple.

The confusion starts when we want to go the other way from viewmodel (or postmodel or editmodel) to domain model, especially when updating objects . We can't use automated/two-way mapping because:

  1. we would have to unflat the flattened hierarchy
  2. all properties on the domain model would have to be mutable/have public setters
  3. the changes coming from the view isn't always just flat properties being mapped back to the domain, but sometimes need to call methods like " ChangeManagerForEmployee() " or similar.

This is also described in Jimmy Bogards article: The case for two-way mapping in AutoMapper , but the solution to this isn't described in detail, only that they go:

From EditModel to CommandMessages – going from the loosely-typed EditModel to strongly-typed, broken out messages. A single EditModel might generate a half-dozen messages.

In a similar SO question there is an answer by Mark Seeman where he mentions that

We use abstract mappers and services to map a PostModel to a Domain Object

but the details - the conceptual and technical implementation - is left out.

Our idea right now is to:

  1. Recieve a FormCollection in the controller's action method
  2. Get the original domain model and flatten it to viewModelOriginal and viewModelUpdated
  3. Merging the FormCollection into viewModelUpdated using UpdateModel()
  4. Use some generic helper method to compare viewModelOriginal with viewModelUpdated
  5. Either A) Generate CommandMessages a la Jimmy Bogard or B) Mutate the differences directly into the domain model through properties and methods (maybe mapping the 1-1 properties directly through AutoMapper)

Can someone provide some examples of how they come from FormCollection through editmodel/postmodel to domain model? "CommandMessages" or "abstract mappers and services"?

I use the following pattern:

[HttpPost]
public ActionResult Update(UpdateProductViewModel viewModel)
{
    // fetch the domain model that we want to update
    Product product = repository.Get(viewModel.Id);

    // Use AutoMapper to update only the properties of this domain model
    // that are also part of the view model and leave the other properties unchanged
    AutoMapper.Map<UpdateProductViewModel, Product>(viewModel, product);

    // Pass the domain model with updated properties to the DAL
    repository.Update(product);

    return RedirectToAction("Success");
}

You might want to consider CQRS(Command Query Responsibility Segregation - I think this might be the concept you were missing), possibly even with Event Sourcing.

It is basically a practice of separating the logic of reading from a data source and writing to a data source, might even mean having different data models for reading and writing.

This might be a good place to start: http://abdullin.com/cqrs/

Option C: Put it all in the controller action. Next, if that gets hairy, decompose into services (abstract mappers) or messages-as-methods (the command message way).

Command message way:

public ActionResult Save(FooSaveModel model) {
    MessageBroker.Process(model);

    return RedirectToAction("List");
}

And the processor:

public class FooSaveModelProcessor : IMessageHandler<FooSaveModel> {

    public void Process(FooSaveModel message) {
        // Message handling logic here
    }

}

This is really just about moving the "processing" of the form out of the controller action and into individual, specialized handlers.

But, I'd only really go this route if controller actions get hairy. Otherwise, just take the form and do the appropriate updates against the domain models as necessary.

There are some similarities here with what I've been doing. My hierarchy of view models is only somewhat flattened from its domain object equivalents, but I do have to deal with calling explicit service methods on save to do things like adding to child collections, changing important values etc, rather than simply reverse mapping. I also have to compare before and after snapshots.

My save is Ajax posted as JSON to an MVC action and enters that action magically bound back to a view model structure by MVC. I then use AutoMapper to transform the top level view model and its descendants back into its equivalent domain structure. I have defined a number of custom AutoMapper ITypeConverters for those cases where a new child item has been added on the client (I'm using Knockout.js) and I need to call an explicit service method. Something like:

            foreach (ChildViewModel childVM in viewModel.Children)
        {
            ChildDomainObject childDO = domainObject.Children.Where(cdo => cdo.ID.Equals(childVM.ID))).SingleOrDefault();
            if (childDO != null)
            {
                Mapper.Map<ChildViewModel, ChildDomainObject>(childVM, childDO);
            }
            else
            {
                MyService.CreateChildDO(someData, domainObject); // Supplying parent
            }
        }

I do a similar thing for deletes and this process cascades quite nicely down through the whole structure. I guess a flattened structure could be either easier to work with or harder - I have an AbstractDomainViewModel with an ID with which I do the above matching, which helps.

I need to do comparisons before and after updating because my service layer calls trigger validation which can affect other parts of the object graph, and this dictates what JSON I need to return as the Ajax response. I only care about changes which are relevant to the UI, so I transform the saved domain object back to a new view model and then have a helper method to compare the 2 view models, using a combination of manual upfront checks and reflection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM