带继承的asp.net核心构造函数注入

ibu*_*ubi 1 c# inheritance dependency-injection asp.net-core

在我的asp.net核心应用程序中,我具有依赖项类,这些依赖项类注入了几乎所有服务。因此,我想构建一个基础服务类来获取对属性的这些依赖关系,并且我的服务继承该基础服务类。

public abstract class BaseService
{
    protected Foo Foo { get; set; }
    protected Bar Bar { get; set; }

    public BaseService(Foo foo, Bar bar)
    {
        Foo = foo;
        Bar = bar;
    }
}
public class Service : BaseService
{
    public Service(IOtherDependency otherDependency) { }

    public void Method()
    {
        var value = Bar.value;
        Foo.Do(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,对于给定的代码,它警告我使用提供的参数调用基本构造函数,但是它们是将在运行时注入的参数,我不希望这样做。如果添加无参数构造函数,它将不会调用我需要的参数化构造函数。

我不想在继承的服务中调用或定义任何在基本服务(FooBar)中注入的类,该怎么办?

顺便说一句Foo,如果Bar类的寿命很重要,则将类作为单例注入到容器中。

Jan*_*ček 8

好的。让我们的基类带有BaseService一个构造函数,将其依赖项作为参数传递:

public abstract class BaseService
{
    protected IFoo Foo { get; private set; }
    protected IBar Bar { get; private set; }

    public BaseService(IFoo foo, IBar bar)
    {
        Foo = foo;
        Bar = bar;
    }
}

public class Service : BaseService
{
    protected IOtherDependency otherDependency { get; private set; }

    public Service(IOtherDependency otherDependency, IFoo foo, IBar bar)
        : base(foo, bar)
    {
        OtherDependency = otherDependency;
    }

}
Run Code Online (Sandbox Code Playgroud)

当该类的开发人员BaseService发现该类的某些功能应该外包给该类应该依赖的外部服务时,他们现在可以做什么?

在类构造函数中添加另一个INewDependency参数BaseService违反了与派生类开发人员之间的约定,因为他们BaseService显式调用类构造函数并且只期望两个参数,因此在升级到新版本时找不到相应的签名构造函数并且编译失败。在我看来,要求在派生类的代码中重复基类依赖项列表违反了单一来源原则,并且将部分基类功能分配到依赖项后编译失败的结果是这种违规行为。

作为解决方法,我建议将基类的所有依赖项集中到指定密封类的属性中,通过ConfigureServices 注册此类,并将其传递到构造函数参数中。

对派生类执行相同的操作。开发人员向 IServicesCollection 注册每个类。

public abstract class BaseService
{
    protected IFoo Foo { get; private set; }
    protected IBar Bar { get; private set; }

    public BaseService(Dependencies dependencies)
    {
        Foo = dependencies.Foo;
        Bar = dependencies.Bar;
    }

    public sealed class Dependencies
    {
        internal IFoo Foo { get; private set; }
        internal IBar Bar { get; private set; }

        public Dependencies(IFoo foo, IBar bar)
        {
            Foo = foo;
            Bar = bar;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

具有父类依赖关系的对象将由提供子类依赖关系的类属性引用。然而,派生类的代码完全从父类的依赖列表中抽象出来,封装在 BaseService.Dependencies 类型中:

public class Service : BaseService
{
    protected IOtherDependency OtherDependency { get; private set; }

    public Service(Dependencies dependencies) : base(dependencies.BaseDependencies)
    {
        OtherDependency = dependencies.OtherDependency;
    }

    public new sealed class Dependencies
    {
        internal IOtherDependency OtherDependency { get; private set; }
        internal BaseService.Depencencies BaseDependencies { get; private set; }

        public Dependencies(IOtherDependency otherDependency, BaseService.Dependencies baseDependencies)
        {
            OtherDependency = otherDependency;
            BaseDependencies = baseDependencies;
        }
    }    
}
Run Code Online (Sandbox Code Playgroud)

在此设计中,每个类的构造函数都有一个参数,即密封类的实例及其依赖项,继承的依赖项作为属性传递。类依赖项列表封装在Dependencies类中,与类和默认IServiceCollection注册一起提供给消费者。

如果BaseClass开发人员决定将某些功能外包给新的依赖项,则所有必要的更改都将在提供的包中进行,而其使用者则不必更改其代码中的任何内容。


ibu*_*ubi 5

这就是我正在寻找的/sf/answers/3422036971/


我按照上面的说明修改了我的控制器库代码。

对于我在服务中提出的问题;由于它们没有HttpContext像内置Controller基类那样使用IServiceProvider,所以我只将类注入到BaseService中,因此无论我在所有服务中需要什么,都可以通过将该类传递给属性provider.GetService()

public abstract class BaseService
{
    protected Foo Foo { get; set; }
    protected Bar Bar { get; set; }

    public BaseService(IServiceProvider provider)
    {
        Foo = provider.GetService<Foo>();
        Bar = provider.GetService<Bar>();
    }
}
public class Service : BaseService
{
    public Service(IOtherDependency otherDependency, IServiceProvider provider) : base(provider) { }

    public void Method()
    {
        var value = Bar.value;
        Foo.Do(value);
    }
}
Run Code Online (Sandbox Code Playgroud)


Lib*_*tad 5

这是使用通用基本控制器类的最简单方法:

public abstract class BaseController<T> : Controller
{
    private IFoo _fooInstance;
    private IBar _barInstance;

    protected IFoo _foo => _fooInstance ??= HttpContext.RequestServices.GetService<IFoo>();
    protected IBar _bar => _barInstance ??= HttpContext.RequestServices.GetService<IBar>();
}
Run Code Online (Sandbox Code Playgroud)

如果您使用 Razor 页面:

class BasePageModel<T> : PageModel where T : class
{
    private IFoo _fooInstance;
    private IBar _barInstance;

    protected IFoo _foo => _fooInstance ??= HttpContext.RequestServices.GetService<IFoo>();
    protected IBar _bar => _barInstance ??= HttpContext.RequestServices.GetService<IBar>();
}
Run Code Online (Sandbox Code Playgroud)