简体   繁体   English

Autofac 的通用 DBContext 注入

[英]Generic DBContext Injection with Autofac

I'm trying to register and inject DbContext instances into my components through Autofac.我正在尝试通过 Autofac 将 DbContext 实例注册并注入到我的组件中。 I know that the approach i'm currently using can be improved and i'm looking for ideas on how to achieve that too.我知道我目前使用的方法可以改进,我也在寻找如何实现这一目标的想法。 At the moment I'm just playing around with the idea of a generic and trying to get it to work but it is throwing this exception.目前我只是在玩通用的想法并试图让它工作,但它抛出了这个异常。 Is it possible Autofac is spawning multiple threads to inject the component? Autofac 是否有可能产生多个线程来注入组件?

System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at UserService.Repositories.UserRepository.FindByIdAsync(String id) in /Users/ryanr/Documents/invoicer-backend-microservices/src/UserService/Repositories/UserRepository.cs:line 31
   at UserService.Queries.Handlers.GetUserByIDQueryHandler.Handle(GetUserByIDQuery query) in /Users/ryanr/Documents/invoicer-backend-microservices/src/UserService/Queries/Handlers/GetUserByIDQueryHandler.cs:line 19
   at UserService.Controllers.UsersController.GetUserById(GetUserByIDQuery query) in /Users/ryanr/Documents/invoicer-backend-microservices/src/UserService/Controllers/UsersController.cs:line 43
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

I have a class library that registers the Autofac types and defines the DBContext interface:我有一个类库,用于注册 Autofac 类型并定义 DBContext 接口:

    public interface IDbContext<T> where T: class
    {
        DbSet<T> DataSet { get; set; }
    }

The Autofac module for registering Repositories and injecting the DbContext用于注册存储库和注入 DbContext 的 Autofac 模块

    public class RepositoryModule : Autofac.Module
    {
        public Assembly ExecutingAssembly { get; set; }
        public string ConnectionString { get; set; }

        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(ExecutingAssembly)
            .Where(t => t.Name.EndsWith("Repository"))
            .AsImplementedInterfaces();

            builder.RegisterAssemblyTypes(ExecutingAssembly)
                .Where(t => t.IsClosedTypeOf(typeof(IDbContext<>)))
                .AsSelf()
                .InstancePerLifetimeScope();

        }
    }

This then gets resolved and injected into my UserService(ASP.Net):然后得到解决并注入到我的 UserService(ASP.Net) 中:

public class UserRepository : IUserRepository
    {
        private readonly UserDBContext _dbContext;
        public UserRepository(UserDBContext dbContext)
        {
            _dbContext = dbContext;
        }


        public async Task<User> FindByIdAsync(string id)
        {
            return await _dbContext.DataSet.FirstOrDefaultAsync(user => user.Id == id);

        }
}

The DbContext class DbContext 类

public class UserDBContext : DbContext, IDbContext<User>
    {
        public DbSet<User> DataSet { get; set; }

        public UserDBContext()
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("server=localhost,1434;user id=sa;password=password;database=UserManagement;");
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<User>().HasKey(m => m.Id);
            builder.Entity<User>().ToTable("User");
            base.OnModelCreating(builder);
        }
   }

The repository then gets injected into my into my QueryHandler and that is where the DBContext is used:然后将存储库注入到我的 QueryHandler 中,这就是使用 DBContext 的地方:

public class GetUserByIDQueryHandler : IQueryHandler<GetUserByIDQuery, User>
    {
        private IUserRepository _repository;
        public GetUserByIDQueryHandler(IUserRepository repository)
        {
            _repository = repository;
        }

        public async Task<User> Handle(GetUserByIDQuery query)
        {
            return await _repository.FindByIdAsync(query.Id);
        }
    }

Where QueryHandlers are resolved like: QueryHandlers 的解析方式如下:

var handlers = scope.Resolve<IEnumerable<IQueryHandler<TRequest, TResponse>>>().ToList();

EDIT: I have reason to believe this is a Dependency Injection/an Autofac problem, because this trivial test ended up working:编辑:我有理由相信这是一个依赖注入/一个 Autofac 问题,因为这个微不足道的测试最终成功了:

// GET api/users/5
        [HttpGet("{Id}", Name = "GetUserById")]
        public async Task<IActionResult> GetUserById([FromBody]GetUserByIDQuery query)
        {
            //var users = await _queryBus.Query<GetUserByIDQuery, User>(query);
            //string id = new Guid().ToString();
            //var users = await _dbContext.DataSet.FirstOrDefaultAsync(x => x.Id == id);
            using(var dbContext = new UserDBContext())
            {
                return Ok(await dbContext.DataSet.ToListAsync());
            }
        }

Any help would be appreciated!任何帮助,将不胜感激! Thanks :)谢谢 :)

I don't think this has anything to do with dependency injection or autofac.我认为这与依赖注入或 autofac 没有任何关系。 Look at the stack trace--the top of the trace says:看看堆栈跟踪——跟踪的顶部说:

at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(Boolean errorsExpected, CancellationToken cancellationToken)在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(布尔错误预期,CancellationToken 取消令牌)

This looks like a problem connecting to the database.这看起来像连接到数据库的问题。 Check your connection string and do a quick test to make sure you can connect to the database without the complexity overhead of autofac or query handlers.检查您的连接字符串并进行快速测试,以确保您可以连接到数据库,而没有 autofac 或查询处理程序的复杂性开销。

I solved this.我解决了这个问题。

The QueryHandler was trying to resolve an async function synchronously, and it was causing problems. QueryHandler 试图同步解析异步函数,但它导致了问题。

For example:例如:

var handlers = scope.Resolve<IEnumerable<IQueryHandler<TRequest, TResponse>>>().ToList();
return await handlers[0].Handle(query);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM