I have an ASP.NET MVC 3 application.
I have a Model
, ViewModel
, View
, Controller
.
I use Ninject
as IoC.
My Controller
uses a ViewModel
to pass data to the View
.
I've started to use Service
s (concrete and interface types) to take information from the ViewModel
and query it against the database to manipulate it.
Can I use the same Service
to setup the ViewModel
? Or is this going against the grain of the design pattern?
Ie Can I abstract setting up the ViewModel
in the Service
layer?
Scenario
The scenario is; my Model
has lots of references to other Models
, so when I setup the ViewModel
in the controller it's to verbose, and I feel the Controller
is doing too much. So I want to be able to just do something like:
var vm = _serviceProvider.SetupViewModel(Guid model1Id, Guid model2Id, /*etc..*/)
And the SetupViewModel
function in the ServiceProvider
would look like this:
public MyModelViewModel SetupViewModel(Guid model1Id, Guid model2Id, /*etc...*/)
{
var vm = new MyModelViewModel();
var model1 = _repository.Model1s.FirstOrDefault(x => x.Id.Equals(model1Id));
var model2 = _repository.Model2s.FirstOrDefault(x => x.Id.Equals(model2Id));
// etc....
vm.Model1 = model1;
vm.Model2 = model2;
return vm;
}
By doing this I could also add some null
conditions as well, not worrying about making my Controller
really really really big!!
I use 1 ViewModel
for the Create/Edit actions. I don't reuse the ViewModel
elsewhere.
I would let the service layer return a Domain Model and map it to a ViewModel in the controller.
This way you can use a service method with multiple ViewModels, for a desktop and mobile view for example.
You can let AutoMapper do the hard work for you or do it manually, by creating a constructor in the ViewModel which takes the Domain Model.
The domain model:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Telephone { get; set; }
public string Email { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
The ViewModel:
public class CustomerWithOrdersModel
{
public CustomerWithOrdersModel(Customer customer)
{
Id = customer.Id;
FullName = string.Format("{0}, {1}", customer.LastName, customer.FirstName);
Orders = customer.Orders.ToList();
}
public int Id { get; set; }
public string FullName { get; set; }
public IEnumerable<Order> Orders { get; set; }
}
EDIT: AutoMapper example:
The AutoMapper profile containing the mapping from a Customer
to a CustomerWithOrdersModel
:
public class ViewModelProfile : Profile
{
public override string ProfileName
{
get { return "ViewModel"; }
}
protected override void Configure()
{
CreateMap<Customer, CustomerWithOrdersModel>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => string.Format("{0}, {1}", src.LastName, src.FirstName)))
.ForMember(dest => dest.Orders, opt => opt.MapFrom(src => src.Orders.ToList()));
}
}
Id
is mapped by convention.
Extension methods for the ViewModelProfile
:
public static class ViewModelProfileExtensions
{
public static CustomerWithOrdersModel ToModel(this Customer customer)
{
return Mapper.Map<CustomerWithOrdersModel>(customer);
}
public static Customer ToEntity(this CustomerWithOrdersModel customerWithOrdersModel)
{
return Mapper.Map<Customer>(customerWithOrdersModel);
}
}
The controller action:
public ActionResult Details(int customerId)
{
Customer customer = _customerService.GetById(customerId);
CustomerWithOrdersModel customerWithOrders = customer.ToModel();
return View(customerWithOrders);
}
If you create a mapping from CustomerWithOrdersModel
to Customer
, you can use customerWithOrdersModel.ToEntity()
to map it back to the Domain Model.
Thats it! You can remove the constructor with the Customer
domain model from the ViewModel.
If you have the view models as their own project and handle the mapping and returning of view models in your service layer, I see nothing wrong with that. For separation of concerns you could always have another component that handles the mapping.
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.