使用 autofac 将构造函数注入到基类中 [英] Constructor injection into a base class using autofac

查看:30
本文介绍了使用 autofac 将构造函数注入到基类中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个抽象的基本控制器,它有一个构造函数,我希望在构建控制器时由 autofac 填充.

I have an abstract base controller which has a constructor I hoped would be populated by autofac when the controllers were built.

public abstract class BaseController : Controller
{
    protected ILogger { get; private set; }

    protected BaseController()
    {
    }

    protected BaseController(ILogger logger)
    {
        Logger = logger;
    }
}

当我从中派生控制器时,这似乎不起作用.

This doesn't seem to work when I derive a controller from it.

只有当我从控制器显式调用构造函数时,我才能让它工作.这是正确的做法吗?

I can only get this to work when I explicitly call the constructor explicitly from the controller. Is this the correct way to do this?

public class PublicController : BaseController
{
    public PublicController()
    {
    }

    public PublicController(ILogger logger) : base(logger)
    {

    }
}

此外,使用 MVC 集成程序集,似乎没有办法为其他类共享容器来进行自己的解析.我在某处读到不鼓励这样做,为什么不呢?这只是为了解耦任何单个 ioc 框架的依赖关系吗?构造函数注入是唯一在层次结构中填充依赖项的方法吗?

Also, using the MVC integration assembly, there doesn't seem to be a way to share the container for other classes to do their own resolving. I read somewhere that this is not encouraged, why not? Is this just to decouple the dependency of any single ioc framework? Is constructor injection the only way to populate dependencies down the hierarchy?

推荐答案

显式调用基类构造函数是在 C# 中使用构造函数注入实现此目的的唯一方法.看起来您应该从 BaseControllerPublicController 中删除无参数构造函数,因为当记录器可用时,它们实际上不应该被调用.

Calling the base class constructor explicitly is the only way to do this using constructor injection in C#. It looks like you should remove the parameterless constructors from BaseController and PublicController as they should never actually be called when a logger is available.

将依赖项注入基本控制器的问题是使用 ASP.NET MVC 和 IoC 的常见问题.有多种选择/思想流派.

The problem of injecting dependencies into a base controller is a common one using ASP.NET MVC and IoC. There are several options/schools of thought.

1.) 使用聚合服务.为了保持派生类构造函数的简单性,创建一个单一的服务来暴露或委托基础控制器所需的所有不同服务(例如 IBaseControllerDependencies 或类似的).然后将这个服务传递给 BaseController 就像您在此处使用 ILogger 所做的一样.

1.) Use aggregate services. To keep derived class constructors simple, create a single service that exposes or delegates to all the different services needed by the base controller (e.g. IBaseControllerDependencies or similar.) Then pass this one service to the BaseController just as you are doing with ILogger here.

根据您的应用程序和您使用的基类数量,有各种优点/缺点.搜索Autofac 聚合服务"以了解更多相关信息.

There are various pros/cons depending on your application and the number of base classes you're using. Google for 'Autofac aggregate services' to see more on this.

2.) 使用属性注入.公开基类上的 ILogger 属性,并使用以下命令配置容器:

2.) Use property injection. Make the ILogger property on your base class public, and configure the container using:

builder.RegisterControllers().PropertiesAutowired();

属性注入在 Autofac 中并不是真正的首选技术.构造函数的角色是接受依赖,而可写属性通常被视为代码异味,因此 Autofac 并没有真正针对这种情况进行优化.缺点之一是不应该注入的可写属性经常被错误地注入,导致奇怪的后果.

Property injection isn't really a preferred technique in Autofac. The constructor's role is to accept dependencies, while writeable properties are often seen as a code smell, so Autofac doesn't really optimise for this case. One of the drawbacks is that writeable properties that shouldn't be injected often are mistakenly, with odd consequences.

3.) 将基本控制器功能重构为各种动作过滤器.Autofac 可以将动作过滤器注入到 MVC 动作调用管道中.因此,过滤器可以采用基类上的依赖项,并且可以以横切方式应用相同的关注点.网络上关于此的更多信息,ExtensibleActionInvoker.InjectActionInvoker() 指向您需要的信息.并非总是可以解决所有问题.

3.) Refactor base controller functionality into various action filters. Autofac can inject action filters into the MVC action invocation pipeline. Thus the filters can take the dependencies that were on the base class, and the same concerns might be applied in a cross-cutting way. More info on this out on the web, ExtensibleActionInvoker and .InjectActionInvoker() point to the info you'd need. Not always possible with all concerns.

4,也是第二个问题的答案.)使用来自 DependencyResolver.Current 的服务位置解析基本控制器依赖项.

4, also the answer to your second question.) Resolve base controller dependencies using service location from DependencyResolver.Current.

var logger = DependencyResolver.Current.GetService<ILogger>();

不鼓励这样做的原因是它使生成的应用程序更难理解,因为不再可能通过查看一个地方(构造函数)来查看组件所依赖的服务.确定必须配置的内容在容器中使用特定组件之前,必须查看组件的整个代码库以找到 GetService() 调用.单元测试时的明显障碍.

The reason this isn't encouraged is that it makes the resulting application harder to understand because it is no longer possible to see what services a component depends upon by looking in one place (the constructor.) To determine what must be configured in the container before a particular component can be used, one has to look at the entire codebase of the component to find the GetService() calls. A noticeable impediment when unit testing.

希望这会有所帮助,我知道有点脑残:) 其他人可能可以添加更多想法.

Hope this helps, bit of a brain dump I know :) Others can probably add some more ideas to these.

这篇关于使用 autofac 将构造函数注入到基类中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆