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