我应该在使用 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 Stream
或 SafeHandle
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);
}
然后您可以决定创建的实例是否应该负责处理上下文,或者创建者是否需要保持控制。
当然,如果创建的实例要取得控制权,你需要妥善处置这个实例。
我在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 Stream
或 SafeHandle
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);
}
然后您可以决定创建的实例是否应该负责处理上下文,或者创建者是否需要保持控制。
当然,如果创建的实例要取得控制权,你需要妥善处置这个实例。