ViewModels, CQRS and Entities

2019-05-21 12:36发布

问题:

I am trying to understand how to probably make use of a view model, a command and the database entity

At the moment I think there is a lot of manual mapping between this, and I am unsure if I should use a tool like AutoMapper to map ViewModel <-> Command <-> Entity when there is a lot of properties (like 10-15).

Take this example (written quickly in notepad, may not compile - in my real applicaiton I use dependency injection and IoC):

public class Student {
    public int Id { get; set; }
    public string Name { get; set; }

    public string AnoterProperty { get; set; }
    public string AnoterProperty2 { get; set; }     
    public string AnoterProperty3 { get; set; }    
    public string AnoterProperty4 { get; set; }    
    public string AnoterProperty5 { get; set; }    

    public int StudentTypeId { get; set; }
    public StudentType StudentType { get; set; } // one-to-many
}

public class CreateStudentViewModel {
    public string Name { get; set; }
    public DropDownList StudentTypes { get; set; }
}

public class DropDownList {
    public string SelectedValue { get; set; }
    public IList<SelectListItem> Items { get; set; }
}

public class CreateStudent {
    public string Name { get; set; }
    public int StudentTypeId { get; set; }
}

public class HandleCreateStudentCommand {

    public void Execute(CreateStudent command) {
        var student = new Student {
            Name = command.Name,
            StudentTypeId = command.StudentTypeId
        };

        // add to database
    }

}

public class StudentController {

    public ActionResult Create() {
        var model = new CreateStudentViewModel {
            StudentTypes = new DropDownList { 
               Items = // fetch student types from database 
            }
        };

        return View(model);
    }

    public ActionResult Create(CreateStudentViewModel model) {
        var command = new CreateStudent {
            Name = model.Name,
            StudentTypeId = Convert.ToInt32(model.Items.SelectedValue);
        };

        var commandHandler = new HandleCreateStudentCommand();
        commandHandler.Execute(command);
    }

}

What worries me here is that I do a lot of manual mapping between the different parts. And this example only contains a few properties.

I am especially worried about a possible update command which will most likely contain all possible properties of the student entity.

Is there a neat solution, or should I go with AutoMapper and map from ViewModel <-> Command and Command <-> Entity?

回答1:

One possible way to deal with less classes and mappings / projections / conversions:

For all the views used in the WRITE-SIDE of your application (the views that allow the user to submit a form or so), let the Command to be their Model (View Model).

That is, you can have:

[HttpPost]
public ActionResult Create(CreateStudent command) {
    commandHandler.Execute(command);
}

As for the get action, I see that you have to populate a drop-down list...

[HttpGet]
public ActionResult Create() {
    // var model = create the view model somehow
    return View(model);
}

Now, as for the model in the latter snippet, you may have these options (and perhaps others):

  • let the Command object (CreateStudent) be the model of the view (as the view is a... view for a command, a view for a request) and pass the items for the drop-down using the ViewBag
  • derive from CreateStudent command just to make of it a view model that can also hold the items for the drop-down

Notice that there is no need to add the suffix "Command" to your command object. Just give it a name that means doSomething - a verb phrase (verb + object) - and it should be fine.

In the end, some command objects really are the models for some views - no need to define another view-models for those and then have a lot of duplication etc.

Also, you may find these interesting:

  • http://cqrs.nu/Faq
  • http://cqrsjourney.github.io/
  • https://github.com/mspnp/cqrs-journey-code/tree/master/source/Conference