In an asp.net core mvc project, i have an abstract ViewModel class, for a layout, which requires some properties to function properly, and each concrete page's ViewModel derives from it:
public abstract class Layout
{
    // required to function properly
    public string Prop1 { get; set; }
    // required to function properly
    public IEnumerable<Foo> FooList { get; set; }
    protected Layout()
    {
        // Populate FooList from an util caching class?
    }
}
public class ViewModelHome : Layout {}
public class ViewModelProducts : Layout {}
The layout requires a FooList, which is populated from a database or a cache, since it is data not often changed.
I would like to avoid to putting each required field in the constructor in order to make it not too verbose, and I would like to avoid the following for each derived class:
var model = new ViewModelHome();
model.FooList = .....
In asp.net core caching is available through DI of IMemoryCache, so I cannot write something like this:
    public abstract class Layout
    {           
       protected Layout()
       {
           var cacheClass = new MyCacheClass(...IMemoryCache??...);
           this.FooList = cacheClass.GetFooList();        
       }        
    }
This is because I'm creating Layout on my own in a service, in turn called from controllers.
public class MainController : Controller
{
    private readonly IMainService _service;
    public MainController(IMainService service)
    {
       _service = service;
    }
    public IActionResult Home()
    {              
       return View(_service.GetHomeViewModel());
    }
    public IActionResult Products()
    {
        return View(_service.GetProductsViewModel());
    }
 }
My question is: should i put in the abstract class constructor the logic for getting data from database or cache?
Thanks
This is a bad design to have dependencies in view models. My understanding is that you want to setup some common properties for all your view models. In order to resolve this I would recommend you to implement some kind of initializer (e.g. IViewModelInitializer) which then can be injected in a controller and then called for all view models in order to initialize them. You can inject all the dependencies (caching, repositories etc) in this initializer. See the code sample below:
public class Foo { }
public class Bar { }
public abstract class Layout
{
    public Bar Bar { get; set; }
    public IEnumerable<Foo> FooList { get; set; }
}
public class HomeViewModel : Layout
{
}
public interface IFooRepository
{
    IEnumerable<Foo> GetList();
}
public interface IBarRepository
{
    Bar GetSingle();
}
public interface ILayoutInitializer
{
    void Initialize(Layout layout);
}
public class LayoutInitializer : ILayoutInitializer
{
    private readonly IFooRepository _fooRepository;
    private readonly IBarRepository _barRepository;
    public LayoutInitializer(IFooRepository fooRepository, IBarRepository barRepository)
    {
        _fooRepository = fooRepository;
        _barRepository = barRepository;
    }
    public void Initialize(Layout layout)
    {
        if (layout is null) throw new ArgumentNullException(nameof(layout));
        layout.FooList = _fooRepository.GetList();
        layout.Bar = _barRepository.GetSingle();
    }
}
public class MainController : Controller
{
    private readonly ILayoutInitializer _layoutInitializer;
    public MainController(ILayoutInitializer layoutInitializer)
    {
        _layoutInitializer = layoutInitializer;
    }
    public IActionResult Home()
    {
        var homeViewModel = new HomeViewModel();
        _layoutInitializer.Initialize(homeViewModel);
        return View(homeViewModel);
    }
}
                        I guess you have a couple of options here:
IMemoryCache into the property via DI (most containers support it), i would not recommend this approach.public class Foo {
   [Inject] // or other marker attribute specific to your IoC
   public readonly IMemoryCache;
....
}
public Foo(): base()
{
  base.MemoryCache = IoC.Resolve<IMemoryCache>();
}
public Foo(IMemoryCache cache): base(cache)
public void Foo(IMemoryCache cache, int bar)
public Foo(IBar bar, IBaz baz, IContainer container): base(container)
{
  ...
}
I had instances where option 5 worked best during heavy refactorings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With