我应该在使用 Entity Framework Core 时处理 DbContext

Should I dispose DbContext when using Entity Framework Core

我在aspx文件(.NET Framework 4.6.2项目)中有如下方法:

public static string SetAvgPeriodDays(int userId)
{
    var service = new AveragePeriodDaysService(EfHelper.GetContext());

    try
    {
        return service.SetAveragePeriodDays(userId);
    }
    catch (Exception e)
    {
        return e.Message;
    }
}

AveragePeriodDaysService class 有一个接受 DbContext 实例的构造函数:

public class AveragePeriodDaysService
{
    private readonly MyDbContext _ctx;

    public AveragePeriodDaysService(MyDbContext ctx)
    {
        _ctx = ctx;
    }

    public string SetAveragePeriodDays(int userId)
    {
        // main logic goes here...
    }
}

这里 EfHelper class:

public class EfHelper
{   
    public static MyDbContext GetContext()
    {
        var options = new DbContextOptionsBuilder<MyDbContext>();

        var connectionString = ...
        options.UseSqlServer(connectionString);
        options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);

        return new MyDbContext(options.Options);
    }
}

问题。我应该处理 MyDbContext 吗?如果是,我该如何正确操作?

作为一条规则,你永远不应该处理你从外部获得的依赖项,因为你永远不知道还有谁使用这个依赖项的相同实例。这意味着 AveragePeriodDaysService 无法处理 MyDbContext。但是 SetAvgPeriodDays 确切地知道谁将使用 MyDbContext 因为它请求创建 MyDbContext,因此它可以并且应该在使用后处理它。使用 using 关键字:

public static string SetAvgPeriodDays(int userId)
{
    using(var ctx = EfHelper.GetContext())
    {
        var service = new AveragePeriodDaysService(ctx);

        try
        {
            return service.SetAveragePeriodDays(userId);
        }
        catch (Exception e)
        {
            return e.Message;
        }
    }
}

我实际上不太确定正确的模式是什么,但我通常采用以下方法(类似于 .NET StreamSafeHandle class确实如此):

您的 AveragePeriodDaysService 似乎确实控制了上下文(它将其存储在 private readonly 字段中)。所以实际上这个 class 应该实现 IDisposable 并处理上下文本身。

另一方面,您可能希望对不同的 "service" class 使用同一个上下文,而不必总是创建一个新上下文。因此,如果这些 classes 总是处理上下文,那将很烦人。

所以我的实现方式是这样的:

public class AveragePeriodDaysService : IDisposable
{
    private readonly MyDbContext _ctx;
    private readonly bool _ownContext;

    public AveragePeriodDaysService(MyDbContext ctx, bool ownContext)
    {
        _ctx = ctx;
        _ownContext = ownContext;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) GC.SuppressFinalize(this);
        if (_ownContext) _ctx.Dispose();
    }

    public void Dispose()
    {
        Dispose(true);
    }

然后您可以决定创建的实例是否应该负责处理上下文,或者创建者是否需要保持控制。

当然,如果创建的实例要取得控制权,你需要妥善处置这个实例。