Com*_*ity 4 c# multithreading entity-framework
我目前正在编写一个依赖于数据库的应用程序,而我正在使用Entity Framework(根据Nuget版本为6.1.1).
现在我编写了一个Repository模式,如下所示:
public class RepositoryBase<TEntity> where TEntity : class
{
#region Constructors
protected RepositoryBase(IDbContext context, IUnitOfWork unitOfWork)
{
Context = context;
DbSet = Context.Set<TEntity>();
UnitOfWork = unitOfWork;
}
#endregion
#region Properties
protected IDbSet<TEntity> DbSet;
protected readonly IDbContext Context;
protected readonly IUnitOfWork UnitOfWork;
#endregion
#region Methods
protected TEntity Get(Expression<Func<TEntity, bool>> filter)
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
return !query.Any() ? null : !query.Where(filter).Any() ? null : query.First(filter);
}
protected TEntity Get(Expression<Func<TEntity, bool>> filter, string[] includeProperties)
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
includeProperties.Each(x => query = query.Include(x));
return !query.Any() ? null : !query.Where(filter).Any() ? null : query.First(filter);
}
protected virtual IQueryable<TEntity> GetAll()
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
return query.AsQueryable();
}
protected IQueryable<TEntity> GetAll(string[] includeProperties)
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
includeProperties.Each(x => query = query.Include(x));
return query.AsQueryable();
}
protected IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> filter)
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
query = DbSet.Where(filter);
return query;
}
protected IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> filter, string[] includeProperties)
{
DbSet.ThrowIfNull("DbSet");
IQueryable<TEntity> query = DbSet;
query = DbSet.Where(filter);
includeProperties.Each(x => query = query.Include(x));
return query;
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
我确实有其他类继承自此存储库,但我暂时将它们排除在范围之外.
现在,当我在存储库中执行Get操作时,我检索数据,但是当我执行2时会在sime时间调用(我只使用相同的URL打开2个浏览器窗口并执行它们),然后弹出以下错误:
EntityFramework.SqlServer.dll中出现"System.Data.Entity.Core.EntityException"类型的异常,但未在用户代码中处理
附加信息:基础提供程序在Open上失败.
这里有一个小图片可以提供尽可能详细的信息:

现在,我知道默认情况下Entity Framwork DbContext不是线程安全的,所以我知道我应该做些什么来使它成为线程安全的,但我只是不知道如何做到这一点.
anybode可以提供我如何实现这一目标的任何指导吗?另外,我对IDisposeable没有多少经验,所以如果你建议在某个地方实现这个,那么你应该非常友好地向我提供一些如何实现它的信息.
值得一提的是我使用Unity来管理我的对象,你可以在这里找到配置:
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
Run Code Online (Sandbox Code Playgroud)
我的类型的注册在这里完成:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IDbContext, OxygenDataContext>();
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
}
Run Code Online (Sandbox Code Playgroud)
类'OxygenDataContext'是我对'DbContext'和'IDbContext'的实现.该代码可以在下面找到(剪切,我不发布我的所有元素):
public class OxygenDataContext : DbContext, IDbContext
{
#region Constructors
public OxygenDataContext()
: base("Oxygen")
{
Configuration.ProxyCreationEnabled = false;
}
#endregion
#region IDbContext Members
public IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
public void SaveChanges()
{
base.SaveChanges();
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
更新
根据我的说法,因为我正在使用它而导致这种行为的Unity:
container.RegisterType<IDbContext, OxygenDataContext>();
Run Code Online (Sandbox Code Playgroud)
这意味着不会在每个请求上创建OxygenDataContext的新实例.
谢谢您的帮助.
更新2
当我修改Unity确认时,如下所示:
container.RegisterType<IDbContext, OxygenDataContext>(new PerRequestLifetimeManager());
Run Code Online (Sandbox Code Playgroud)
我认为只有一个单一实例,但似乎它不起作用,因为我一直收到同样的错误.
更新3
我已经更改了Unity配置以使用以下代码解析IDbContext,这是一个带有'PerResolveLifetimeManager'的DbContext:
container.RegisterType<IDbContext, OxygenDataContext>(new PerResolveLifetimeManager());
Run Code Online (Sandbox Code Playgroud)
这意味着每个解析调用应该创建一个'OxygenDataContext'的新实例,或者我不正确?
在我的UnitOfWork的构造函数中,我正在分配如下的存储库:
/// <summary>
/// Creates a new instance of the <see cref="IUnitOfWork"/>.
/// </summary>
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param>
public UnitOfWork(IDbContext context)
: base(context)
{
versioningRepository = new Repository<Versioning, IDbContext>(context, this);
settingRepository = new Repository<Setting, IDbContext>(context, this);
siteRepository = new VersionedRepository<Site, int, IDbContext>(context, this);
pageRepository = new VersionedRepository<Page, int, IDbContext>(context, this);
layoutRepository = new VersionedRepository<Layout, int, IDbContext>(context, this);
assemblyRepository = new VersionedRepository<Assembly, int, IDbContext>(context, this);
logRepository = new Repository<Log, IDbContext>(context, this);
}
Run Code Online (Sandbox Code Playgroud)
所以这应该意味着每个存储库都获得了它自己唯一的'IDbContext'实例,但它不能按预期工作.
更新4
我已经设法解决了我自己的问题,请耐心等待,因为我正在编写解决方案作为这篇文章的答案.
更新5
它似乎不起作用.在这里看到我认为有效的解决方案:
我调整我的IUnitOfWork接口来实现IDisposeable接口,然后重新调整实现到以下内容:
protected virtual void Dispose(bool disposing)
{
if (disposing) { Context = null; }
}
public void Dispose()
{
Dispose(true);
}
Run Code Online (Sandbox Code Playgroud)
但是,它不起作用.我在这里有点迷失,所以如果有人知道解决方案:-)
更新6
根据一些帖子,我需要提供一些有关如何实例化我的存储库的信息.所以这是解释:
我正在使用包含所有存储库的UnitOfWork:
/// <summary>
/// Creates a new instance of the <see cref="IUnitOfWork"/>.
/// </summary>
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param>
public UnitOfWork(IDbContext context)
: base(context)
{
versioningRepository = new Repository<Versioning>(context, this);
settingRepository = new Repository<Setting>(context, this);
siteRepository = new VersionedRepository<Site, int>(context, this);
pageRepository = new VersionedRepository<Page, int>(context, this);
layoutRepository = new VersionedRepository<Layout, int>(context, this);
assemblyRepository = new VersionedRepository<Assembly, int>(context, this);
logRepository = new Repository<Log>(context, this);
}
Run Code Online (Sandbox Code Playgroud)
我也尝试将代码更改为以下内容,但这也不起作用:
/// <summary>
/// Creates a new instance of the <see cref="IUnitOfWork"/>.
/// </summary>
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param>
public UnitOfWork(IDbContext context)
: base(context)
{
versioningRepository = new Repository<Versioning>(context, this);
settingRepository = new Repository<Setting>(new OxygenDataContext(), this);
siteRepository = new VersionedRepository<Site, int>(new OxygenDataContext(), this);
pageRepository = new VersionedRepository<Page, int>(new OxygenDataContext(), this);
layoutRepository = new VersionedRepository<Layout, int>(new OxygenDataContext(), this);
assemblyRepository = new VersionedRepository<Assembly, int>(new OxygenDataContext(), this);
logRepository = new Repository<Log>(new OxygenDataContext(), this);
}
Run Code Online (Sandbox Code Playgroud)
我仍然收到错误消息:"基础提供程序在打开时失败",内部异常显示"连接未关闭.连接的当前状态正在连接."
您会看到UnitOfWork的构造函数确实接受了一个IDbContext实例,并且所有存储库都是使用该实例构建的.
我正在使用Unity,我的IDbContext的注册采用以下代码:
container.RegisterType<IDbContext, OxygenDataContext>();
Run Code Online (Sandbox Code Playgroud)
在我的Unity配置中,我也注册了IUnitOfWork接口:
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
Run Code Online (Sandbox Code Playgroud)
我确实感觉错误是在Unity注册或UnitOfWork中的某个地方,但我似乎没有找到它.
如果我收到你正确的信息,你就会在线程中共享相同的DbContext.这不是DbContext的用途.以这种方式共享数据非常困难.
我不喜欢以"但你为什么需要它"的形式给出答案,但这篇文章看起来就像那样.
我建议你重新设计解决方案并保持DbContext对象的生命周期 - 做一个操作并处理DbContext.执行工作单元,提交/回滚并完成存储库.不要忘记DbContext将跟踪它所提取的所有对象.您可能不希望跟踪对象在内存中的使用寿命比使用它们的事务长.
让数据库成为从多个线程共享数据的点.
| 归档时间: |
|
| 查看次数: |
4165 次 |
| 最近记录: |