C# ASP.Net 与 Autofac 集成时面临的核心问题
C# ASP.Net Core facing issues when integrating with Autofac
我有一个控制器 DailyExpenseController,我正在将 IQueryExecutor class 注入控制器。
DailyExpenseController
using HomeBudgetTrackingSystem.CrossCutting;
using HomeBudgetTrackingSystem.DTO;
using HomeBudgetTrackingSystem.Models;
using HomeBudgetTrackingSystem.Query;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace HomeBudgetTrackingSystem.Controllers
{
[Produces("application/json")]
[Route("api/DailyExpense/{Id}")]
public class DailyExpenseController : Controller
{
private readonly IQueryExecutor queryExecutor;
public DailyExpenseController(IQueryExecutor queryExecutor)
{
this.queryExecutor = queryExecutor;
}
[HttpPost]
[Route("Create")]
public async Task<IActionResult> CreateExpense(long Id, [FromBody] Expenditure expenditure)
{
var findEntity = new FindEntityQuery<Expense>(Id);
var find = queryExecutor.Execute(findEntity);
/*if (find == null)
return NotFound();*/
var createExpenses = new CreateExpenditure(expenditure);
var expenses = queryExecutor.Execute(createExpenses);
return Ok(expenses);
}
/*If I write this method, it will result in 500 Error, because this method has id which is confusing
with the variable Id declared with Controller*/
/*[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}*/
[HttpGet("{id2}")]
public string Get(long id)
{
return "value";
}
[HttpPost]
public void Post([FromBody]string value)
{
}
}
}
以下是我的 Autofac 配置详细信息
Startup.cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using HomeBudgetTrackingSystem.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace HomeBudgetTrackingSystem
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
//public void ConfigureServices(IServiceCollection services)
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
/*services.AddDbContext<BudgetContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BudgetConnection")
)); */
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new RepositoryHandlerModule());
//builder.RegisterType<QueryExecutor>().As<IQueryExecutor>();
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
public IContainer ApplicationContainer { get; private set; }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
appLifetime.ApplicationStopped.Register(() => ApplicationContainer.Dispose());
}
}
}
下面是我的模型构建器class,我在其中注册了依赖关系
RepositoryHandlerModule.cs
using Autofac;
using HomeBudgetTrackingSystem.CrossCutting;
namespace HomeBudgetTrackingSystem.Repository
{
public class RepositoryHandlerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<QueryExecutor>().As<IQueryExecutor>().InstancePerRequest();
}
}
}
我没有对 Program.cs
进行任何更改
Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace HomeBudgetTrackingSystem
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
以下是我收到的异常详细信息
Autofac.Core.DependencyResolutionException: Unable to resolve the type 'HomeBudgetTrackingSystem.CrossCutting.QueryExecutor' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
- HomeBudgetTrackingSystem.CrossCutting.IQueryExecutor
Details ---> No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. (See inner exception for details.) ---> Autofac.Core.DependencyResolutionException: No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself.
at Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope)
at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable1 parameters)
--- End of inner exception stack trace ---
at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable
1 parameters)
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable
1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable
1 parameters)
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
我已经提到了关于这些的多个链接。突出的是
http://autofac.readthedocs.io/en/latest/faq/per-request-scope.html
http://autofac.readthedocs.io/en/latest/integration/aspnetcore.html
(我更喜欢没有容器的配置)
http://www.codedigest.com/posts/49/using-autofac-instead-of-inbuilt-di-container-with-in-aspnet-core-mvc(这是我提到的)
谁能帮我解决这些问题?
我找到了问题的解决方案。
http://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html
这里在标题下'Differences From ASP.NET Classic',列出了一个关键点
使用 InstancePerLifetimeScope 而不是 InstancePerRequest。
我也对 RepositoryHandlerModule.cs 进行了更改,例如添加 BudgetContext.cs
如下
public class RepositoryHandlerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<QueryExecutor>().As<IQueryExecutor>().InstancePerLifetimeScope();
builder.RegisterType<BudgetContext>().InstancePerLifetimeScope();
}
}
我有一个控制器 DailyExpenseController,我正在将 IQueryExecutor class 注入控制器。
DailyExpenseController
using HomeBudgetTrackingSystem.CrossCutting;
using HomeBudgetTrackingSystem.DTO;
using HomeBudgetTrackingSystem.Models;
using HomeBudgetTrackingSystem.Query;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace HomeBudgetTrackingSystem.Controllers
{
[Produces("application/json")]
[Route("api/DailyExpense/{Id}")]
public class DailyExpenseController : Controller
{
private readonly IQueryExecutor queryExecutor;
public DailyExpenseController(IQueryExecutor queryExecutor)
{
this.queryExecutor = queryExecutor;
}
[HttpPost]
[Route("Create")]
public async Task<IActionResult> CreateExpense(long Id, [FromBody] Expenditure expenditure)
{
var findEntity = new FindEntityQuery<Expense>(Id);
var find = queryExecutor.Execute(findEntity);
/*if (find == null)
return NotFound();*/
var createExpenses = new CreateExpenditure(expenditure);
var expenses = queryExecutor.Execute(createExpenses);
return Ok(expenses);
}
/*If I write this method, it will result in 500 Error, because this method has id which is confusing
with the variable Id declared with Controller*/
/*[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}*/
[HttpGet("{id2}")]
public string Get(long id)
{
return "value";
}
[HttpPost]
public void Post([FromBody]string value)
{
}
}
}
以下是我的 Autofac 配置详细信息
Startup.cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using HomeBudgetTrackingSystem.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace HomeBudgetTrackingSystem
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
//public void ConfigureServices(IServiceCollection services)
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
/*services.AddDbContext<BudgetContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BudgetConnection")
)); */
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new RepositoryHandlerModule());
//builder.RegisterType<QueryExecutor>().As<IQueryExecutor>();
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
public IContainer ApplicationContainer { get; private set; }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
appLifetime.ApplicationStopped.Register(() => ApplicationContainer.Dispose());
}
}
}
下面是我的模型构建器class,我在其中注册了依赖关系
RepositoryHandlerModule.cs
using Autofac;
using HomeBudgetTrackingSystem.CrossCutting;
namespace HomeBudgetTrackingSystem.Repository
{
public class RepositoryHandlerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<QueryExecutor>().As<IQueryExecutor>().InstancePerRequest();
}
}
}
我没有对 Program.cs
进行任何更改Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace HomeBudgetTrackingSystem
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
以下是我收到的异常详细信息
Autofac.Core.DependencyResolutionException: Unable to resolve the type 'HomeBudgetTrackingSystem.CrossCutting.QueryExecutor' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration: - HomeBudgetTrackingSystem.CrossCutting.IQueryExecutor
Details ---> No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. (See inner exception for details.) ---> Autofac.Core.DependencyResolutionException: No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. at Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope) at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable
1 parameters) --- End of inner exception stack trace --- at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable
1 parameters) at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters) at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable
1 parameters) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable
1 parameters) at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired) at lambda_method(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
我已经提到了关于这些的多个链接。突出的是
http://autofac.readthedocs.io/en/latest/faq/per-request-scope.html http://autofac.readthedocs.io/en/latest/integration/aspnetcore.html (我更喜欢没有容器的配置) http://www.codedigest.com/posts/49/using-autofac-instead-of-inbuilt-di-container-with-in-aspnet-core-mvc(这是我提到的)
谁能帮我解决这些问题?
我找到了问题的解决方案。
http://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html
这里在标题下'Differences From ASP.NET Classic',列出了一个关键点 使用 InstancePerLifetimeScope 而不是 InstancePerRequest。
我也对 RepositoryHandlerModule.cs 进行了更改,例如添加 BudgetContext.cs 如下
public class RepositoryHandlerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<QueryExecutor>().As<IQueryExecutor>().InstancePerLifetimeScope();
builder.RegisterType<BudgetContext>().InstancePerLifetimeScope();
}
}