首页 > 解决方案 > .NetCore 数据库 ILoggerProvider 存储库上下文 null

问题描述

我正在尝试构建一个自定义 LoggerProvider,它通过存储库/dbcontext 将所有日志写入数据库。我在 ConfigureServices 中添加了日志记录,并在 Configure 中添加了新的提供程序,如下所示

public void ConfigureServices(IServiceCollection services)
        {
            //Register EntityFramework Core Datacontext for Dependency Injection
            services.AddDbContext<DataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            //Register Repositories for Dependency Injection
            services.AddScoped<ILogRepository, LogRepository>();
            services.AddScoped<ICountryRepository, CountryRepository>();

            services.AddLogging();      

            //Add CORS
            services.AddCors(options =>
            {
                options.AddPolicy("Open", builder =>
                {
                    builder.AllowAnyOrigin();
                    builder.AllowAnyHeader();
                    builder.AllowAnyMethod();
                });
            });

            services.AddControllers()
                    .AddMvcOptions(o =>
                    {
                        //Allow XML as a request Accept type
                        o.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
                    });

            
            
        }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory log, ILogRepository logRepo)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //Add Database Logging Provider
            log.AddProvider(new LoggerDatabaseProvider(logRepo));

            app.UseHttpsRedirection();      

            app.UseRouting();

            app.UseAuthorization();

            app.UseCors("Open");

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

我创建了一个标准 LogRepository,它使用在 ConfigureServices 中注册的数据上下文

public class LogRepository : ILogRepository, IDisposable
    {
        private DataContext _dataContext;

        public LogRepository(DataContext dataContext)
        {
            _dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
        }

        public async Task<IEnumerable<Log>> GetAllAsync()
        {
            return await _dataContext.Logs.ToListAsync();
        }

        public async Task<Log> GetByIdAsync(int id)
        {
            return await _dataContext.Logs.FirstOrDefaultAsync(c => c.Id == id);
        }

        public async Task<Log> CreateAsync(Log log)
        {
            var addedEntity = _dataContext.Logs.Add(log);
            await _dataContext.SaveChangesAsync();
            return addedEntity.Entity;
        }

        public async Task UpdateAsync(int id, Log log)
        {
            if (!await ExistsAsync(id))
                return;

            _dataContext.Entry(log).State = EntityState.Modified;
            await _dataContext.SaveChangesAsync();
        }

        public async Task DeleteAsync(int id)
        {
            var foundEntity = await GetByIdAsync(id);
            if (foundEntity == null)
                return;

            _dataContext.Logs.Remove(foundEntity);
            await _dataContext.SaveChangesAsync();
        }

        public async Task<bool> ExistsAsync(int id)
        {
            return await _dataContext.Logs.AnyAsync(c => c.Id == id);
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if(disposing)
            {
                if(_dataContext != null)
                {
                    _dataContext.Dispose();
                    _dataContext = null;
                }
            }
        }
    }

这是 ILogger 的实现

public class LoggerDatabaseProvider : ILoggerProvider
    {
        private ILogRepository _repo;

        public LoggerDatabaseProvider(ILogRepository repo)
        {
            _repo = repo;
        }

        public ILogger CreateLogger(string categoryName)
        {
            return new Logger(categoryName, _repo);
        }

        public void Dispose()
        {
        }

        public class Logger : ILogger
        {
            private readonly string _categoryName;
            private readonly ILogRepository _repo;

            public Logger(string categoryName, ILogRepository repo)
            {
                _repo = repo;
                _categoryName = categoryName;
            }

            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }

            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                    RecordMsg(logLevel, eventId, state, exception, formatter);
            }

            private void RecordMsg<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                _repo.CreateAsync(new Log
                {
                    LogLevel = logLevel.ToString(),
                    CategoryName = _categoryName,
                    Message = formatter(state, exception),
                    CreatedDate = DateTime.Now
                });
            }

            public IDisposable BeginScope<TState>(TState state)
            {
                return new NoopDisposable();
            }

            private class NoopDisposable : IDisposable
            {
                public void Dispose()
                {
                }
            }
        }
    }

我从我的 APIController 调用记录器,如下所示

private readonly ICountryRepository _repository;
        private readonly ILogger _logger;

        public CountriesController(ICountryRepository countryRepository, ILogger logger)
        {
            _repository = countryRepository ?? throw new ArgumentNullException(nameof(countryRepository));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        // GET: api/Country
        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            _logger.LogInformation($"{nameof(_repository)}: GetAll");
            var countries = await _repository.GetAllAsync();
            return Ok(countries);
        }

我在我的 LogRepository 上放置了一个断点,当我到达那个断点时,我看到有一个 DataContext。但是,当调用 LogInformation 并查看存储库时,DataContext 为 NULL。我错过了什么?

标签: logging.net-coreasp.net-core-webapi

解决方案


根据您的代码,我发现您已经在 LogRepository 类中设置了 Dispose 方法来处理 dbcontext。

这就是调用 LogRepository 方法时 dbcontext 为 null 的原因。

我建议您可以删除_dataContext.Dispose让 DI 管理 LogRepository 和 dbcontext 的方法,然后它会运行良好。

更多细节,您可以参考以下代码:

public class LogRepository : ILogRepository, IDisposable
{
    private TestDbcontext _dataContext;

    public LogRepository(TestDbcontext dataContext)
    {
        _dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
    }

    public async Task<IEnumerable<Log>> GetAllAsync()
    {
        return await _dataContext.Logs.ToListAsync();
    }

    public async Task<Log> GetByIdAsync(int id)
    {
        return await _dataContext.Logs.FirstOrDefaultAsync(c => c.Id == id);
    }

    public async Task<Log> CreateAsync(Log log)
    {
        var addedEntity = _dataContext.Logs.Add(log);
        await _dataContext.SaveChangesAsync();
        return addedEntity.Entity;
    }

    public async Task UpdateAsync(int id, Log log)
    {
        if (!await ExistsAsync(id))
            return;

        _dataContext.Entry(log).State = EntityState.Modified;
        await _dataContext.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var foundEntity = await GetByIdAsync(id);
        if (foundEntity == null)
            return;

        _dataContext.Logs.Remove(foundEntity);
        await _dataContext.SaveChangesAsync();
    }

    public async Task<bool> ExistsAsync(int id)
    {
        return await _dataContext.Logs.AnyAsync(c => c.Id == id);
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        //if (disposing)
        //{
        //    if (_dataContext != null)
        //    {
        //        _dataContext.Dispose();
        //        _dataContext = null;
        //    }
        //}
    }
}

推荐阅读