将 DbContext 和 ILogger 注入数据库 DAL 构造函数,最佳实践

Injecting DbContext and ILogger into database DAL constructor, best practice

我正在使用 Entity Framework.Entity Framework 在 ASP.NET Core 3.1 中构建 Web API

我的数据库上下文在 Startup.cs:

中注册为服务
services.AddDbContext<LocalDbContext>(options =>
{
    options.UseSqlServer(builderLocal.ConnectionString);
});

我正在使用 DI 在我的控制器中检索 DbContext,当我为每个方法实例化它时将其传递到我的数据库访问-class (DAL)

private readonly LocalDbContext _context;

public HomeController(LocalDbContext context)
{
    _context = context;
}

public IActionResult GetSomeData(int id)
{
    var localDb = new LocalDb(_context);
    return Ok(localDb.GetSomeDataById(id));
}

然后是我的数据库文件:

public class LocalDbContext : DbContext
{
    public LocalDbContext(DbContextOptions<LocalDbContext> options)
        : base (options) { }

    **DbSets for my models**
}

public class LocalDb
{
    private readonly LocalDbContext _context;
    private readonly ILogger<LocalDb> _logger;

    // I would want to avoid having to pass in logger in this contstructor
    public LocalDb(LocalDbContext context) 
    {
        _context = context;
        // Can I retrieve a logger from somewhere else? From the context?
        // _logger = logger; 
    }

    public void AddStudent(Student student)
    {
        _context.Student.Add(student);
        try
        {
          _context.SaveChanges();  
        } 
        catch (Exception ex)
        {
            _logger.LogError("logMessage about exception: " + ex.StackTrace);
            throw;
        }
    }
}

所以我希望可以写入 _logger,但在我的代码中我没有将 _logger 设置为任何值。我知道 ILogger 存在于 DbContext 中,并且有很多指南解释了如何为上下文和 EF 添加和配置记录器。但是当我想使用我的 LocalDb class 时,我是否必须在每次调用 var localDb = new LocalDb(_context, _logger)

时传入一个 ILogger 实例

我觉得每次都要传入一个logger实例有点奇怪,必须有更好的解决方案。是否可以将 LocalDb class 添加为服务,然后依赖项将上下文和记录器注入到 class 中?最佳做法是什么?

请注意 HomeController 不直接使用 LocalDbContext,而只是将其传递给它的 real 依赖项,LocalDb.因此,你不应该将 LocalDbContext 注入到 HomeController 的构造函数中,而是直接注入 LocalDb。这优雅地解决了你的问题,因为现在 LocalDb 可以在 DI 容器中注册,它可以为你解决它可能具有的任何依赖关系。

这是一个直接依赖于 LocalDbHomeController 示例。

public class HomeController : Controller
{
    private readonly LocalDb _db;

    public HomeController(LocalDb db)
    {
        _db = db;
    }

    public IActionResult GetSomeData(int id)
    {
        return Ok(_db.GetSomeDataById(id));
    }
}

因为LocalDb被注入到构造函数中,所以必须在DI Container中注册:

services.AddTransient<LocalDb>();
services.AddDbContext<LocalDbContext>(options =>
{
    options.UseSqlServer(builderLocal.ConnectionString);
});

但是由于LocalDb是由DI Container组成的,它可以用任何依赖来扩展,比如ILogger依赖:

public class LocalDb
{
    private readonly LocalDbContext _context;
    private readonly ILogger<LocalDb> _logger;

    public LocalDb(LocalDbContext context, ILogger<LocalDb> _logger) 
    {
        _context = context;
        _logger = logger; 
    }
    ...
}

提示: 防止在您的代码库中散布 catch 记录和重新抛出的语句。相反,更喜欢有一些全局基础设施来记录所有失败的请求。如果我没记错的话 ASP.NET Core 会为您开箱即用。如果没有,只需几行代码即可启用。这使代码(例如您的 LocalDb.AddStudent 更简单,并限制了 class 具有的依赖项数量。