我的以下查询存在异步问题。我有单例上下文,我试图执行以下查询:
var query = await (from parent in Context.ParentTable
join child in Context.ChildTable
on parent.ID equals child.ID
into allResult
from ResultValue in allResult.DefaultIfEmpty()
where ResultValue.TenantId == tenantId
select new Result
{
Code = parent.Code,
Type = parent.Type,
ID = ResultValue == null ? 0 : ResultValue.Id
}).ToListAsync();
我的单身语境如下:
public class BaseRepository
{
private readonly IConfigurationContextFactory configurationContextFactory;
private IConfigurationContext context;
protected IConfigurationContext Context
{
get
{
return context ?? (context = configurationContextFactory.Context);
}
}
public BaseRepository(IConfigurationContextFactory configurationContextFactory)
{
this.configurationContextFactory = configurationContextFactory;
}
}
配置上下文工厂返回Context,如下所示:
private ConfigurationContext Get()
{
logger.WriteEntrySync(LogLevel.Information,
null != context ? "Config Context: Using existing context." : "Config Context: Wiil create new context.");
return context ?? (context = new ConfigurationContext(connectionString));
}
在此我遇到以下错误的间歇性问题:
第二个操作在此前一个上下文之前开始 异步操作完成。使用'等待'确保任何 在调用另一个方法之前已完成异步操作 在这种背景下。任何实例成员都不能保证是线程 安全
答案 0 :(得分:21)
我有单身上下文
这是你的问题。 DbContext
不是线程安全的,并且旨在一次执行一个查询。由于您正在共享DbContext
,因此您可能会尝试同时调用另一个查询,这不是合法的"以DbContext
条款。
您甚至可以在remarks of ToListAsync
:
在同一上下文中进行多个活动操作 实例不受支持。使用'等待'确保任何 在调用另一个方法之前已完成异步操作 在这种情况下。
所做的不是通过全局单例重复使用您的上下文,每次要查询数据库时都创建一个新的上下文。
修改强>
不是通过工厂方法获得单个Context
,而是为每个查询分配一个新的:{/ p>
using (var context = new ConfigurationContext(connectionString))
{
var query = await (from feature in context.Features
join featureFlag in context.FeatureFlags
on feature.FeatureId equals featureFlag.FeatureId
into allFeatures
from featureFlagValue in allFeatures.DefaultIfEmpty()
where featureFlagValue.TenantId == tenantId
select new BusinessEntities.FeatureFlag
{
Code = feature.Code,
Type = feature.Type,
FeatureFlagId = featureFlagValue == null ? 0 : featureFlagValue.FeatureFlagId
}).ToListAsync();
}
答案 1 :(得分:9)
DbContext
应该适用于一项商业交易(工作单位),不能少于此。
业务事务通常是请求,页面或表单。 DbContext不是线程安全的,它保留了一个内部实体缓存并跟踪更改,因此您无法在多个请求中共享它。
您确实需要阅读实体框架文档:Working with DbContext。
我不会为每个存储库创建一个实例,因为业务事务可以操作多个存储库。
您可以在存储库中注入上下文:
public class BaseRepository
{
private IConfigurationContext context;
public BaseRepository(IConfigurationContext context)
{
this.context = context;
}
//...
}
更改工厂,以便每次都创建一个实例:
public interface IConfigurationContextFactory
{
IConfigurationContext CreateContext();
}
// ...
public ConfigurationContext CreateContext()
{
return new ConfigurationContext(connectionString);
}
然后配置依赖项解析程序,为每个工作单元注入IConfigurationContext
。让我们假设您正在使用统一的ASP.NET应用程序。
container.RegisterType<IConfigurationContext>(
//Instance per http request (unit of work)
new PerRequestLifetimeManager(),
//Create using factory
new InjectionFactory(c => c.Resolve<IConfigurationContextFactory>.CreateContext()));
请勿在需要时忘记在业务交易结束时致电SaveChangesAsync
:操作成功,需要保留修改。