Autofac 属性注入

发布于 2024-12-08 08:46:29 字数 1753 浏览 0 评论 0原文

我正在更改我的 Asp.Net MVC3 项目以使用 Autofac 将服务注入到我的控制器中。到目前为止,这非常简单。我的服务都有一个 Telerik OpenAccess db 属性,我通过构造函数注入该属性(在服务基类中)。我的控制器都具有也被注入的服务的构造函数属性。

我有一个名为 AuditInfo 的类,它封装了控制器的可审核属性:

public class AuditInfo
{      
    public string RemoteAddress { get; set; }

    public string XForwardedFor { get; set; }

    public Guid UserId { get; set; }

    public string UserName { get; set; }
}

我的服务类中的 OpenAccess db 属性需要注入此类的实例,以便在各种数据库调用中用作审核信息。

问题是,这不是一个可以在Application_Start 处实例化的类,因为它的至少两个属性RemoteAddress 和XForwardedFor 在OnActionExecuting 的最早阶段(即一旦Request 变量存在)就被填充。

因此,我在 BaseController 类的 OnActionExecuting 方法中实例化它,如下所示:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);
    db.AuditInfo = AuditInfo;                                      
}

public AuditInfo AuditInfo
{
    get
    {
        return new AuditInfo()
        {
            RemoteAddress = this.Request.ServerVariables["REMOTE_ADDR"],
            XForwardedFor = this.Request.ServerVariables["X_FORWARDED_FOR"],
            UserId = this.UserId,
            UserName = this.UserName
        };
    }
}

所以 - 我的问题是:

  1. 我不喜欢直接访问 OnActionExecuting 中的 OpenAccess db 属性。
  2. 我希望将此 AuditInfo 基本上注入到任何
  3. 我不认为可以使用 AuditInfo 的构造函数注入的任何 AuditInfo 属性中,因为服务依赖于数据库 - 控制器依赖于服务 - 数据库依赖于 AuditInfo 但 AuditInfo 在以下情况之前不可用控制器被实例化并收到第一个请求。 =>循环依赖...

我将如何设置 autofac 将 AuditInfo 注入到任何将其作为属性的类中?或者是否有更好的方法来回避循环依赖并使用某种形式的 lambda/惰性构造函数属性?

即使许多请求可能是同一会话的一部分并且没有不同的 IP 地址/用户信息,但 AuditInfo 在每次请求时都可能不必要地重新初始化,这是否值得关注?

谢谢

I am in the process of changing my Asp.Net MVC3 project to use Autofac for service injection into my controllers. So far this has been pretty straightforward. My services all have a Telerik OpenAccess db property which I inject through the constructors (In a service base class). And my controllers all have constructor properties for services which also get injected.

I have a class called AuditInfo which encapsulates auditable properties of a controller:

public class AuditInfo
{      
    public string RemoteAddress { get; set; }

    public string XForwardedFor { get; set; }

    public Guid UserId { get; set; }

    public string UserName { get; set; }
}

My OpenAccess db property in my service classes needs to have an instance of this class injected in to it in order to use as auditing information in various database calls.

The problem is that this is not a class that can be instantiated at Application_Start because at least two properties of it, RemoteAddress and XForwardedFor are populated at the earliest stage of OnActionExecuting, i.e. once the Request variables exist.

Therefore, I instantiate this in the OnActionExecuting method of my BaseController class as such:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);
    db.AuditInfo = AuditInfo;                                      
}

public AuditInfo AuditInfo
{
    get
    {
        return new AuditInfo()
        {
            RemoteAddress = this.Request.ServerVariables["REMOTE_ADDR"],
            XForwardedFor = this.Request.ServerVariables["X_FORWARDED_FOR"],
            UserId = this.UserId,
            UserName = this.UserName
        };
    }
}

So - my problem/questions are:

  1. I don't like this direct reach in to the OpenAccess db property in OnActionExecuting.
  2. I'd like this AuditInfo basically to be injected in to any AuditInfo property anywhere
  3. I don't think I can use constructor injection for AuditInfo because Services depend on db - controllers depend on services - db depends on AuditInfo BUT AuditInfo is not available until a controller is instantiated and received its first request. => circular dependency...

How would I setup autofac to inject AuditInfo in to any class that has it as a property? Or is there a better way of sidestepping the circular dependency and using some form of lambda/lazy constructor properties?

Is it at all concerning that AuditInfo gets re-initialized potentially unnecessarily at every request even though a lot of requests may be part of the same session and not have different ip address/user info?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ぃ弥猫深巷。 2024-12-15 08:46:29

事实证明,Autofac 的 MVC 集成可以解析 HttpRequestBase。因此您不需要直接引用HttpContext.Current.Request

Autofac 的实现使用 HttpContext.Current 在幕后。这是有效的,因为 MVC 框架在您的代码(或 Autofac 的)运行之前设置 HttpContext.Current。因此不存在循环依赖 - 请求“自然存在”在 HttpContext.Current.Request 上,就像在控制器中一样。 (这个问题解释了如何进行)

因此,您可以按照 Steven 的建议创建一个 IAuditInfoFactory,但在其构造函数中要求一个 HttpRequestBase,而不是使用 HttpContext.Current(如果这能让您感觉更好)不是引用静态变量。

此外,不存在循环依赖,如果需要,您可以通过构造函数注入 AuditInfo

builder.Register(c => c.Resolve<IAuditInfoFactory>().CreateNew())
    .As<AuditInfo>()
    .InstancePerHttpRequest();

It turns out Autofac's MVC Integration can resolve an HttpRequestBase for you. So you don't need to reference HttpContext.Current.Request directly.

Autofac's implementation uses HttpContext.Current behind the scenes. This works because the MVC framework sets HttpContext.Current before your code (or Autofac's) runs. So there's no circular dependency - the Request "naturally exists" on HttpContext.Current.Request just as much as in your controller. (This question kind of explains how)

So you could do an IAuditInfoFactory as Steven suggests but demand an HttpRequestBase in its constructor instead of using HttpContext.Current if it makes you feel better about not referencing static variables.

Also, there's no circular dependency and you could constructor-inject the AuditInfo if you want:

builder.Register(c => c.Resolve<IAuditInfoFactory>().CreateNew())
    .As<AuditInfo>()
    .InstancePerHttpRequest();
灯下孤影 2024-12-15 08:46:29

答案是:使用工厂。

IAuditInfoFactory 注入到需要它的类型中,并创建一个如下所示的实现:

public class HttpRequestAuditInfoFactory : IAuditInfoFactory
{
    // Service for requesting information about the current user.
    private readonly ICurrentUserServices user;

    public HttpRequestAuditInfoFactory(ICurrentUserServices user)
    {
        this.user = user;
    }

    AuditInfo IAuditInfoFactory.CreateNew()
    {
        var req = HttpContext.Current.Request;

        return new AuditInfo()
        {
            RemoteAddress = req.ServerVariables["REMOTE_ADDR"],
            XForwardedFor = req.ServerVariables["X_FORWARDED_FOR"],
            UserId = this.user.UserId,
            UserName = this.user.UserName
        };
    }
}

您可以按如下方式注册该类:

builder.RegisterType<HttpRequestAuditInfoFactory>()
    .As<IAuditInfoFactory>()
    .SingleInstance();

现在您可以注入

The answer is: Use a factory.

Inject an IAuditInfoFactory into the type that needs it, and create an implementation like this:

public class HttpRequestAuditInfoFactory : IAuditInfoFactory
{
    // Service for requesting information about the current user.
    private readonly ICurrentUserServices user;

    public HttpRequestAuditInfoFactory(ICurrentUserServices user)
    {
        this.user = user;
    }

    AuditInfo IAuditInfoFactory.CreateNew()
    {
        var req = HttpContext.Current.Request;

        return new AuditInfo()
        {
            RemoteAddress = req.ServerVariables["REMOTE_ADDR"],
            XForwardedFor = req.ServerVariables["X_FORWARDED_FOR"],
            UserId = this.user.UserId,
            UserName = this.user.UserName
        };
    }
}

You can register that class as follows:

builder.RegisterType<HttpRequestAuditInfoFactory>()
    .As<IAuditInfoFactory>()
    .SingleInstance();

Now you can inject

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文