BenAffleckIsBatman BenAffleckIsBatman - 19 days ago 8
C# Question

.NET Design Pattern MVVM with ViewModelService

While researching on the MVVM pattern, I often come across a "services" component in this pattern specially with the new .net core documents, webcasts & learning materials.

Samples have:


  • Index.cshtml (View)

  • HomeController (Controller)

  • ViewModelService (?)

  • IndexViewModel (ViewModel)

  • IndexModel (Model)



Is the usage of an additional abstraction layer (ViewModelService) an extension of the MVVM Pattern and good practice or is there another name for this pattern? Code looks good and clean for me, but most explanation of the MVVM pattern don't use a ViewModelService for creating the ViewModel and passing it to the controller.

Based on the code below, there is a ViewModelService layer instead of passing the ViewModel directly to the controller.

thanks.




UPDATE with code



Index.cshtml

@model FishTank.ViewModels.DashboardViewModel


HomeController.cs

public class HomeController: Controller
{
private readonly IViewModelService viewModelService;

public HomeController(IViewModelService viewModelService)
{
this.viewModelService = viewModelService;
}

public IActionResult Index()
{
ViewBag.Title = "Fish tank dashboard";
return View(viewModelService.GetDashboardViewModel());
}

}


ViewModelService.cs

public class ViewModelService : IViewModelService
{
private readonly ISensorDataService sensorDataService;
private readonly IUrlHelper urlHelper;

public ViewModelService(ISensorDataService sensorDataService, IUrlHelperFactory urlHelperFactory, IActionContextAccessor actionContextAccessor)
{
this.sensorDataService = sensorDataService;
urlHelper = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
}

public DashboardViewModel GetDashboardViewModel()
{
return new DashboardViewModel
{
LastFed = "unknown",

WaterTemperatureTile = new SensorTileViewModel
{
Title = "Water temperature",
Value = sensorDataService.GetWaterTemperatureFahrenheit().Value,
ColorCssClass = "panel-primary",
IconCssClass = "fa-sliders",
Url = urlHelper.Action("GetWaterTemperatureChart", "History")
}
}
};
}
}


DashboardViewModel.cs

public class DashboardViewModel
{
public SensorTileViewModel WaterTemperatureTile { get; set; }
public SensorTileViewModel FishMotionTile { get; set; }
public SensorTileViewModel WaterOpacityTile { get; set; }
public SensorTileViewModel LightIntensityTile { get; set; }

[Display(Name = "Please enter the food amount:")]
public int FoodAmount { get; set; }

[Display(Name = "Last feeding was at: ")]
public string LastFed { get; set; }
}

Answer

Is the usage of an additional abstraction layer (ViewModelService) an extension of the MVVM Pattern and good practice or is there another name for this pattern?

No, this is not per se part of the MVVM-pattern but it is common OO-programming.

In my regard it is a best practice to keep your controllers as lightweight as possible. The "single responsibility" of a controller is, eh, controlling stuff. Thus you don't want to clutter your controllers with logic.

Also I would probably move the creation of the ViewModel to a factory (it is a creational pattern) and let the service fetch data to put in the constructed ViewModel. Once again: strive for single responsibility of your classes.

Something like this:

Factory:

public static class ViewModelFactory
{
    public static DashboardViewModel CreateDashboardViewModel()
    {
        return new DashboardViewModel
        {
            LastFed = "unknown",    
            WaterTemperatureTile = CreateSensorTileViewModel()
        };
    }

    public static SensorTileViewModel CreateSensorTileViewModel()
    {
        return new SensorTileViewModel
        {
            Title = "Water temperature",
            ColorCssClass = "panel-primary",
            IconCssClass = "fa-sliders"
        };
    }
}

ViewModelService:

public class ViewModelService : IViewModelService
{
    private readonly ISensorDataService sensorDataService;
    private readonly IUrlHelper urlHelper;

    public ViewModelService(
               ISensorDataService sensorDataService, 
               IUrlHelperFactory urlHelperFactory, 
               IActionContextAccessor actionContextAccessor)
    {
        sensorDataService = sensorDataService;
        urlHelper = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
    }

    public DashboardViewModel GetDashboardViewModel()
    {
        var model = ViewModelFactory.CreateDashboardViewModel();
        var url = urlHelper.Action("GetWaterTemperatureChart", "History")
        var waterTemp = sensorDataService.GetWaterTemperatureFahrenheit().Value;
        model.Value = waterTemp;
        model.url = url;
        return model;
    }
}

Also, I do not like constructor injection but that is a matter of taste :)

Comments