简体   繁体   中英

ASP.NET MVC Design Pattern Best Practice with Services

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.

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