实体框架异步发出上下文或查询?

时间:2015-04-21 05:33:24

标签: c# entity-framework asynchronous async-await

我的以下查询存在异步问题。我有单例上下文,我试图执行以下查询:

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));
}

在此我遇到以下错误的间歇性问题:

  

第二个操作在此前一个上下文之前开始   异步操作完成。使用'等待'确保任何   在调用另一个方法之前已完成异步操作   在这种背景下。任何实例成员都不能保证是线程   安全

2 个答案:

答案 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:操作成功,需要保留修改。