【问题标题】:Autofac InstancePerRequest returns new isntanceAutofac InstancePerRequest 返回新实例
【发布时间】:2020-12-22 22:41:34
【问题描述】:

我有一个 ASP.NET Web API,.Net 4.6.1 项目,我需要在中间件中捕获一些信息,然后在将从控制器调用的代码中检索它。在 .Net 核心中,将我的自定义上下文类注册为 Scoped 并在消息处理的不同阶段对其进行解析非常容易。在 .Net Framework 中,看起来与它相似的是 Autofac 的 InstancePerRequest,所以我尝试了,但它并没有像我预期的那样工作。显然每次我做BeginScope() 它都会返回一个新实例,即使我在同一个请求中?我正在实现IAutofacContinuationActionFilter 接口,我在其中解析我的服务,注册InstancePerRequest,然后在控制器中我尝试再次解析它并获取新实例。我在这里错过了什么?

哦,在下面的控制器中,两个 IHomeService 实例:通过构造函数注入并手动解析正在创建新实例。

更新: 上面的代码是对真实情况的过度简化。我需要从过滤器传递的信息的调用位于一个单独的类中,并且调用通过一系列自动生成的代码进行。构造函数注入对我来说不是一个选项,所以我希望有一个类似于 .Net Core DI 的解决方案。

我的 WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        var container = MyContainerBuilder.Build(config);
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

我的容器构建器类:

public class MyContainerBuilder
{
    public static IContainer Build(HttpConfiguration config)
    {
        var builder = new ContainerBuilder();

        builder.RegisterWebApiFilterProvider(config);

        builder
            .Register(c => new MyCustomFilter())
            .AsWebApiActionFilterForAllControllers()
            .InstancePerRequest();
        // var assembly = typeof(IHomeService).Assembly;
        // builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().InstancePerRequest();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
        builder
            .RegisterType<HomeService>()
            .As<IHomeService>()
            .InstancePerRequest();

        return builder.Build();
    }
}

过滤器:

public class MyCustomFilter : IAutofacContinuationActionFilter
{
    public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
        HttpActionContext actionContext,
        CancellationToken cancellationToken,
        Func<Task<HttpResponseMessage>> next)
    {
        using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
        {
            var hs = scope.Resolve<IHomeService>();
            ++hs.Counter;
            var hs1 = scope.Resolve<IHomeService>();
            ++hs1.Counter;
            var r = next().Result;
            return await Task.FromResult(r);
        }
    }
}

控制器:

[Route("home")]
public class HomeController : ApiController
{
    public IHomeService HomeService { get; set; }

    public HomeController(IHomeService homeService)
    {
        HomeService = homeService;
    }
 
    [HttpGet]
    [Route("")]
    public string Index()
    {
        var dr = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
        using (var scope = dr.GetRequestLifetimeScope())
        // This does not work either, returns new instance:
        // using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
        {
            var hs = scope.Resolve<IHomeService>();
            ++hs.Counter;
        }
        return "Home";
    }
}

我尝试解决的服务类:

public interface IHomeService
{
    int Counter { get; set; }
}
public class HomeService : IHomeService
{
    public HomeService()
    {
        Console.WriteLine("Yet another instance of HomeService!!!");
    }
    public int Counter { get; set; }
}

提前致谢

【问题讨论】:

  • 为什么不使用构造函数注入而不是手动创建作用域?使用 (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())。我不确定这一行是做什么的,但它似乎创建了一个新的范围,而不是使用 http 请求的范围(为什么有 beginscope?)

标签: asp.net asp.net-web-api autofac


【解决方案1】:

您不能创建自己的请求范围,您需要从请求消息中获取它。在过滤器中,就像:

var scope = actionContext.Request.GetDependencyScope();

显示此内容的示例过滤器是in the Autofac docs

但是,由于您使用的是 Autofac 接口,它们由 Autofac 为每个请求注入 - 如果您的过滤器需要按请求服务,最好将其作为过滤器的构造函数参数。如果您使用 Autofac 过滤器接口,您只需要进行服务定位。

如果您真的需要过滤器中的服务位置,您仍然可以使用构造函数来简化操作 - 将 ILifetimeScope 参数添加到过滤器构造函数中,您将获得请求范围作为参数。

对于控制器,同样的事情:在构造函数中注入你需要的东西,而不是使用服务位置。如果您因为无法转义服务位置而需要请求范围,请在控制器构造函数中注入 ILifetimeScope 或从 HttpRequestMessage 中获取请求生命周期。

【讨论】:

  • 感谢您的回复。不幸的是,由于现有代码强制执行的约束,这对我来说不是一个选择。在实际代码中,这不是发生在控制器中,而是发生在从控制器调用的单独类中。调用是通过一系列自动生成的代码进行的,因此也无法对其进行修改,我也无法进行构造函数注入。唯一的选择是手动解决,但据我了解,Autofac 根本不可能?
  • “手动解析”是服务位置。我为此提供了几个选择。但是,长短是 - 要获得请求范围,您需要请求消息。那是 Web API 的东西和那里的基本架构,而不是 Autofac 特定的限制。您不能创建自己的请求范围,您必须获得提供的请求范围。我认为我的回答解决了所提出的问题;如果不是,那么您在问题中更改了太多内容,并且不能准确表示您实际在做什么。如果是这样,您可能需要提出一个新问题。
  • 我同意,为了简化事情,我走得太远了))我刚刚用新信息更新了问题,尽管我觉得 Autofac 根本不可能
  • 如果你从根本上改变了问题,那就是一个新问题。而且,你看到的挑战不是 Autofac 特有的,这就是 Web API 的工作方式。提出一个新问题,并附上一个显示自动生成代码使用的示例。注意在示例中使用。如果可以,请显示实际的自动生成代码。同时,如果这个答案解决了最初提出的问题,请接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
相关资源
最近更新 更多