Felix Felix - 1 month ago 7
C# Question

Access controller data from ViewModel

I am developing ASP.NET Core application. To keep controllers lean, most of the data manipulation is done in ViewModels. Everything works fine - the two problems, however, are


  1. ViewModels don't have access to ControllerContext information (or I can't figure out how to get it). For example, Session, User and whatever else Controller gets for free.

  2. ViewModels don't accept Dependency Injection (again, or I can't figure out how to pass it along). For example, if I have constructor
    MyController(ApplicationDbContext db)
    I get
    db
    passed without any problems. However, if I have
    ComplexViewModel(ApplicationDbContext db)
    I get
    null
    passed in. Obviously, I have exactly the same
    services.AddDbContext<ApplicationDbContext>()
    in Startup



Right now I am passing whatever is required from Controller to ViewModel explicitly. But it feels that there should be a better way.

Answer

View models are supposed to be simple POCOs to transfer data between the views and action methods. I think it is a bad idea to mix all your business logic (or even data access) to view models. You may consider doing that in services. You can inject this services to your controllers.

For example.

Yo get a User information, you may consider creating a service

public interface IUserService
{
  UserDto GetUser(int id);
}
public class UserService : IUserService
{ 
  IUserDataAccess userDataAccess;
  public UserService(IUserDataAccess userDataAccess)
  {
    this.userDataAccess=userDataAccess;
  }
  public UserDto GetUser(int id)
  {
     // with this.userDataAccess, get a User and map to UserDto
    // to do : return something
  }
}

So your controllers will stay lean

public class UserController : Controller
{
  private readonly IUserService userService;
  public UserController(IUserService userService)
  {
    this.userService = userService;
  }
  public ActionResult Details(int id)
  {
    var userDto= this.userService.GetUser(id);
    return View(userDto);
  }
}

Now you can have a UserDataAccess which query your data and inject that to the UserService class.

With this approach your view model does not have any idea what data access technology you are using. Imagine tomorrow you decided to ditch EF for performance reason and want to switch to Dapper, you simply need to create a new implementation of your IUserDataAccess called "DapperUserDataAccess" and udpate your DI config registration to use that. No other code change :)