EF6 两个上下文与具有两个等待的单个上下文

Posted

技术标签:

【中文标题】EF6 两个上下文与具有两个等待的单个上下文【英文标题】:EF6 two contexts vs. single context with two awaits 【发布时间】:2016-07-02 09:00:53 【问题描述】:

以下哪一种是提高性能的更好方法? 注意:对于某些操作,我最多可以执行五个独立的查询。

var foo = await context.foos.ToListAsync();
var bar = await context.bars.ToListAsync();

对比

var fooTask = context1.foos.ToListAsync();
var barTask = context2.bars.ToListAsync();
await Task.WhenAll(fooTask , barTask);

在没有等待的情况下使用相同的上下文会很棒,但this answer 提到这是不可能的。

【问题讨论】:

第二个对我来说更好看,创建一个任务列表,然后等待所有任务 如果您关心性能,请不要使用 EF。无论如何,第二种方法将允许两个查询同时运行,这是否执行得更好取决于其他因素,并且可能最好通过在预期负载下对代表性环境进行测量来确定。 @Jodrell 那么替代方案是什么? @EʜsᴀɴSᴀᴊᴊᴀᴅ,有几种选择,我会考虑 Dapper github.com/StackExchange/dapper-dot-net 我对并行查询的性能提升与启动多个上下文的成本比较好奇。 【参考方案1】:

正如您所发现的,DbContext 不是线程安全的,因此真正并行运行查询的唯一选择是为每个线程/任务创建一个新的 DbContext。

创建新 DbContext 的开销非常低。 https://msdn.microsoft.com/en-us/library/cc853327.aspx

由于对象将来自不同的 DbContext,为了进一步提高性能,我建议您也使用 NoTracking()

编辑:

我用我拥有的数据库做了一个简单的测试程序:

class Program

    public static void Main(string[] args)
    
        Console.WriteLine("Warming up db context...");

        using (var db = new TestDbContext())
        
            Console.WriteLine(db.AuditLogItems.ToList().Count);
        

        // 1st run
        RunAsync();
        RunTasked();

        // 2nd run
        RunAsync();
        RunTasked();

        Console.ReadKey();
    

    private static void RunAsync()
    
        Task.Run(async () =>
        
            var sw = Stopwatch.StartNew();
            List<AuditLogItem> list1;
            List<AuditLogItem> list2;

            using (var db = new TestDbContext())
            
                list1 = await db.AuditLogItems.AsNoTracking().ToListAsync();
                list2 = await db.AuditLogItems.AsNoTracking().ToListAsync();
            

            sw.Stop();
            Console.WriteLine("Executed 0 in 1ms. | 2", "Async", sw.ElapsedMilliseconds, list1.Count + " " + list2.Count);

        ).Wait();
    

    private static void RunTasked()
    
        Func<List<AuditLogItem>> runQuery = () =>
        
            using (var db = new TestDbContext())
            
                return db.AuditLogItems.AsNoTracking().ToList();
            
        ;

        var sw = Stopwatch.StartNew();
        var task1 = Task.Run(runQuery);
        var task2 = Task.Run(runQuery);

        Task.WaitAll(task1, task2);

        sw.Stop();
        Console.WriteLine("Executed 0 in 1ms. | 2", "Tasked", sw.ElapsedMilliseconds, task1.Result.Count + " " + task2.Result.Count);
    

输出是:

Warming up db context...
5908
Executed Async in 293ms. | 5908 5908
Executed Tasked in 56ms. | 5908 5908
Executed Async in 194ms. | 5908 5908
Executed Tasked in 32ms. | 5908 5908

所以是的,选项 2 更快...

【讨论】:

以上是关于EF6 两个上下文与具有两个等待的单个上下文的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:具有多个上下文的独立持久存储与具有单个上下文的独立持久存储

C# EF6 使用 Unity 对一个上下文进行多次异步调用 - Asp.Net Web Api

如何将子上下文中的 NSManagedObjects 的两个不同实例合并为父上下文中的单个实例

如何在 T4 中实例化 EF6 上下文?

是否可以在单火花上下文中收听两个 dtsreams?

具有继承的 MVC 5 脚手架使用错误的实体集