In MVC can ViewModels access service layer?

1.8k views Asked by At

Currently I'm using DI and service locator pattern to get the instance of Service. (Note that Service just generic term im using, and is nothing but the C# class which calls EF repository and perform data operations. Its NOT WCF service)

Is it okay to have Service instance in ViewModel? If yes, the what's the proper way to pass Service instance?

1>Should the controller pass the service instance to ViewModel. In this case Service properly get disposed when controller get disposed

2>or should the ViewModel get service instance using DI & Service Locator. In this case how service will get disposed?

BaseController

public class BaseController:Controller
{
   private MyDomainService _myDomainServiceInstance = null;

    protected MyDomainService MyDomainServiceInstance
    {
        get
        {
            if (_myDomainServiceInstance == null)
            {
                _myDomainServiceInstance = DefaultServiceLocator.Instance.GetInstance<MyDomainService>();
            }

            return _myDomainServiceInstance;
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (_myDomainServiceInstance != null)
        {
            _myDomainServiceInstance.Dispose();
        }
    }
}

Controller

public class MyController:BaseController
{
    public ActionResult DoSomething()
    {
        var model = new SummaryVM(MyDomainServiceInstance);
    }
}

ViewModel

public class SummaryVM
{
    MyDomainService _myDomainService = null;        

    public SummaryVM(MyDomainService myDomainService)
    {
        //Approache 1: Controller is passing the service instance
        _myDomainService = myDomainService;
    }

    public SummaryVM()
    {
        //Aprooche 2: Use DI & Service locator pattern to get the instance
        _myDomainService = DefaultServiceLocator.Instance.GetInstance<MyDomainService>();
    }

    public int[] SelectedClients { get; set; }

    public string[] SelectedStates { get; set; }


    public IEnumerable<Clients> PreSelectedClients
    {
        get
        {
            if (SelectedClients == null || !SelectedClients.Any())
            {
                return new List<AutoCompletePreSelectedVM>();
            }

            return _myDomainService.GetClients(SelectedClients);
        }
    }
}
2

There are 2 answers

2
Murilo On

I had passed through similar situation. I think it is okay having the domain service get instantiated inside view model. The domain service can implement IDisposable, so I would instantiate it inside the get method instead of create the service as an attribute.

2
BSG On

View models are intended to provide information to and from views and should be specific to the application, as opposed to the general domain. Controllers should orchestrate interaction with repositories, services (I am making some assumptions of the definition of service here), etc and handle building and validating view models, and also contain the logic of determining views to render.

By leaking view models into a "service" layer, you are blurring your layers and now have possible application and presentation specific mixed in with what should focused with domain-level responsibilities.

Just don't mix concepts. If your service deals with view models then it should be a presentation service and be layered over top of the actual Model. View models should be flat and simple DTOs purposed toward binding with the view. They should not be part of a DI container graph because that complicates things and makes reasoning about the code more difficult.