您如何使用 ASP.NET Core 3.1 和 Autofac 中的服务位置解决每个请求的项目?
How do you resolve a per-request item using service location in ASP.NET Core 3.1 and Autofac?
我已使用此代码段来设置我的应用程序:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(Startup.Register)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public static void Register(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserServcice>().InstancePerLifetimeScope();
}
并且我已经按照下面提到的方式使用了它:
public interface IUserServcice
{
public long Tick { get; }
}
public class UserService : IUserServcice
{
private long _tick;
public UserService()
{
_tick = DateTime.Now.Ticks;
}
public long Tick => _tick;
}
public WeatherForecastController(IUserServcice userServcice)
{
// _logger = logger;
iUserServcice = userServcice;
var g = Startup.AutofacContainer.Resolve<IUserServcice>();
tick2 = g.Tick;
}
private async Task Get1()
{
var list = new List<long>();
list.Add(iUserServcice.Tick);
var g=Startup.AutofacContainer.Resolve<IUserServcice>();
list.Add(g.Tick);
list.Add(tick2);
//using (var scope= SorviceLocator.Container.BeginLifetimeScope("t1"))
// {
for (int i = 0; i < 3; i++)
{
await Task.Factory.StartNew(() =>
{
var sr = Startup.AutofacContainer.Resolve<IUserServcice>();
list.Add(sr.Tick);
});
}
// }
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
await Get1();
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
不幸的是,调试的结果就像下面上传的图片一样:
正如您在顶部图片中看到的那样,项目是控制器构造函数注入的结果,其他项目都在控制器内部,我的问题是如何让所有这些项目具有相同的值。
当您使用 ASP.NET Core 时,虽然您可以将 Autofac 作为后备容器,但在大多数情况下,您会在 Startup 之外 直接 放弃使用 Autofac class。您在 Startup 中注册您的东西,但在控制器和其他地方,它都是标准依赖注入(没有 Autofac 引用)和 Microsoft 依赖注入抽象。
这很重要,因为它会帮助您 Google 寻找答案。不要寻找“如何使用 Autofac 执行此操作 ?” - 寻找“我如何在 ASP.NET Core 中执行此操作 ?”
首先,避开服务地点。我明白你在做什么,我明白你在做什么......但你需要使用服务位置来证明问题的事实似乎是一个危险信号。
现在已经不在了:
你要的是HttpContext.RequestServices
。当你有一个控制器时,你将拥有 HttpContext
和 RequestServices
对象,那里有请求生命周期范围。 由 Autofac 支持,但界面是 Microsoft 界面。
You can read about RequestServices
in the Microsoft docs.
private readonly IUserService injected;
public WeatherForecastController(IUserService userService)
{
this.injected = userService;
}
public async Task Get()
{
var located = this.HttpContext.RequestServices.GetService<IUserService>();
// located and injected will be the same instance.
}
如果您需要开始子生命周期范围,那也是 MS DI 的事。您需要 IServiceScopeFactory
。这可以是构造函数依赖项,或者您可以像以前一样使用服务位置。
var scopeFactory = this.HttpContext.RequestServices.GetService<IServiceScopeFactory>();
using(var scope = scopeFactory.CreateScope())
{
// Now you have a scope to work with.
}
如果出于任何原因绝对必须从 IServiceProvider
获取 Autofac 生命周期,您可以解决一个问题。从生命周期范围 returns 本身解析生命周期范围。
var requestScope = this.HttpContext.RequestServices.GetService<ILifetimeScope>();
但是,你会再次注意到我们在这里所做的一切都是使用 Microsoft DI 抽象,所以当你寻找答案时,我建议你看得更广泛,而不是将你的搜索限制在 Autofac .无论您使用哪种后备容器,这个答案基本相同。
我已使用此代码段来设置我的应用程序:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(Startup.Register)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public static void Register(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserServcice>().InstancePerLifetimeScope();
}
并且我已经按照下面提到的方式使用了它:
public interface IUserServcice
{
public long Tick { get; }
}
public class UserService : IUserServcice
{
private long _tick;
public UserService()
{
_tick = DateTime.Now.Ticks;
}
public long Tick => _tick;
}
public WeatherForecastController(IUserServcice userServcice)
{
// _logger = logger;
iUserServcice = userServcice;
var g = Startup.AutofacContainer.Resolve<IUserServcice>();
tick2 = g.Tick;
}
private async Task Get1()
{
var list = new List<long>();
list.Add(iUserServcice.Tick);
var g=Startup.AutofacContainer.Resolve<IUserServcice>();
list.Add(g.Tick);
list.Add(tick2);
//using (var scope= SorviceLocator.Container.BeginLifetimeScope("t1"))
// {
for (int i = 0; i < 3; i++)
{
await Task.Factory.StartNew(() =>
{
var sr = Startup.AutofacContainer.Resolve<IUserServcice>();
list.Add(sr.Tick);
});
}
// }
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
await Get1();
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
不幸的是,调试的结果就像下面上传的图片一样:
正如您在顶部图片中看到的那样,项目是控制器构造函数注入的结果,其他项目都在控制器内部,我的问题是如何让所有这些项目具有相同的值。
当您使用 ASP.NET Core 时,虽然您可以将 Autofac 作为后备容器,但在大多数情况下,您会在 Startup 之外 直接 放弃使用 Autofac class。您在 Startup 中注册您的东西,但在控制器和其他地方,它都是标准依赖注入(没有 Autofac 引用)和 Microsoft 依赖注入抽象。
这很重要,因为它会帮助您 Google 寻找答案。不要寻找“如何使用 Autofac 执行此操作 ?” - 寻找“我如何在 ASP.NET Core 中执行此操作 ?”
首先,避开服务地点。我明白你在做什么,我明白你在做什么......但你需要使用服务位置来证明问题的事实似乎是一个危险信号。
现在已经不在了:
你要的是HttpContext.RequestServices
。当你有一个控制器时,你将拥有 HttpContext
和 RequestServices
对象,那里有请求生命周期范围。 由 Autofac 支持,但界面是 Microsoft 界面。
You can read about RequestServices
in the Microsoft docs.
private readonly IUserService injected;
public WeatherForecastController(IUserService userService)
{
this.injected = userService;
}
public async Task Get()
{
var located = this.HttpContext.RequestServices.GetService<IUserService>();
// located and injected will be the same instance.
}
如果您需要开始子生命周期范围,那也是 MS DI 的事。您需要 IServiceScopeFactory
。这可以是构造函数依赖项,或者您可以像以前一样使用服务位置。
var scopeFactory = this.HttpContext.RequestServices.GetService<IServiceScopeFactory>();
using(var scope = scopeFactory.CreateScope())
{
// Now you have a scope to work with.
}
如果出于任何原因绝对必须从 IServiceProvider
获取 Autofac 生命周期,您可以解决一个问题。从生命周期范围 returns 本身解析生命周期范围。
var requestScope = this.HttpContext.RequestServices.GetService<ILifetimeScope>();
但是,你会再次注意到我们在这里所做的一切都是使用 Microsoft DI 抽象,所以当你寻找答案时,我建议你看得更广泛,而不是将你的搜索限制在 Autofac .无论您使用哪种后备容器,这个答案基本相同。