Asp.net核心MVC依赖注入-构造函数中的必需属性?

时间:2019-07-01 06:51:54

标签: c# asp.net-mvc asp.net-core dependency-injection

在asp.net核心mvc项目中,我有一个抽象的ViewModel类用于布局,需要一些属性才能正常运行,而每个具体页面的{{1} }从中得出:

ViewModel

布局需要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 {} ,因为它是不经常更改的数据,所以它是从数据库或缓存中填充的。

我想避免将每个必填字段放在构造函数中,以使其不太冗长,并且我想避免对每个派生类进行以下操作:

FooList

在asp.net中,核心缓存可以通过var model = new ViewModelHome(); model.FooList = ..... 的DI来获得,所以我不能这样写:

IMemoryCache

这是因为我是在服务中自己创建 public abstract class Layout { protected Layout() { var cacheClass = new MyCacheClass(...IMemoryCache??...); this.FooList = cacheClass.GetFooList(); } } ,然后从控制器中调用。

Layout

我的问题是:我应该将抽象类构造函数中的逻辑从数据库或缓存中获取吗?

谢谢

2 个答案:

答案 0 :(得分:3)

在视图模型中具有依赖项是一个糟糕的设计。我的理解是,您想为所有视图模型设置一些通用属性。为了解决这个问题,我建议您实现某种初始化器(例如IViewModelInitializer),然后可以将其注入控制器中,然后对所有视图模型进行调用以对其进行初始化。您可以在此初始值设定项中注入所有依赖项(缓存,存储库等)。请参见下面的代码示例:

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);
    }
}

答案 1 :(得分:1)

我想您在这里有几个选择:

  1. 属性注入-通过DI(大多数容器支持)将IMemoryCache注入属性中,我不推荐这种方法。
public class Foo {
   [Inject] // or other marker attribute specific to your IoC
   public readonly IMemoryCache;
....
}
  1. 服务定位器-对您的容器使用线程安全的单例引用,并解决依赖关系。我也避免使用此选项。
public Foo(): base()
{
  base.MemoryCache = IoC.Resolve<IMemoryCache>();
}
  1. 从派生到父级的Ctor注入-尽管这样做可能很冗长,但实际上是正确的做法,因为您会清楚地了解合同和相关性。
public Foo(IMemoryCache cache): base(cache)
  1. 方法签名中的传递依赖性-非常明确,但遵循功能性编程原则。例如:问问自己,是整个类需要该依赖关系还是仅是其中一种方法?
public void Foo(IMemoryCache cache, int bar)
  1. Ctor注入,但是如果您有太多依赖项,则将容器传递给父对象
public Foo(IBar bar, IBaz baz, IContainer container): base(container)
{
  ...
}

在重重构期间,我遇到了选项5效果最好的情况。