EF核心-关闭读取器时,无效尝试调用CheckDataIsReady(OutOfMemory)

时间:2018-12-18 21:45:42

标签: entity-framework .net-core out-of-memory entity-framework-core ef-core-2.1

我在SQL Server中有一个表 dbo.Cache ,其中有两列:

  • 键:varchar(50)
  • 值:nvarchar(max)

我打算将大字符串存储在“值”列(> 30mb)中,并在多个线程中查询它们。

因此,当我并行执行9个以上查询时,它会引发抛出异常System.InvalidOperationException : Invalid attempt to call CheckDataIsReady when reader is closed

[Fact]
public void TestMemory()
{
    const string key = "myKey1";
    //re-creating record with Value = large 30mb string
    using (var db = new MyDbContext())
    {
        var existingRecord = db.CachedValues.FirstOrDefault(e => e.Key == key);
        if (existingRecord!=null)
        {
            db.Remove(existingRecord);
            db.SaveChanges();
        }

        var myHugeString = new string('*',30*1024*1024);
        db.CachedValues.Add(new CachedValue() {Key = key, Value = myHugeString});
        db.SaveChanges();
    }

    //Try to load this record in parallel threads, creating new dbContext
    const int threads = 10;
    Parallel.For(1, threads, new ParallelOptions(){MaxDegreeOfParallelism = threads}, (i) =>
    {
        using (var db = new MyDbContext())
        {
            var entity = db.CachedValues.FirstOrDefault(c => c.Key == key);
        }
    });
}

试图在每次读取数据库之前/之后执行GC.Collect(); GC.WaitForFullGCComplete();-没有帮助

试图在较低级别上模仿这种行为,直接通过sqlDataReader.ExecuteReader(CommandBehavior.SequentialAccess)读取数据-它抛出OutOfMemoryException

1 个答案:

答案 0 :(得分:0)

因此,经过调查,我发现这只是一个OutOfMemory问题,因为在第8个并行请求分配30mb * 2(因为它是Unicode字符)之后,.Net在我的应用程序中分配的内存实际上超过了1.2GB,这足以满足我的工作站.net运行时开始因内存不足(https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#ephemeral-generations-and-segments)而烦人。

话虽如此,我只能重试读取(内存分配)操作,直到成功迫使GC收集在catch块中为止。

Retry.Action(() =>
{
    using (var db = new MyDbContext())
    {
        var entity = db.CachedValues.FirstOrDefault(c => c.Key == key);
    }
}).OnException((OutOfMemoryException exception) =>
{
    GC.Collect();
    GC.WaitForFullGCComplete();
    return true;

})
.OnException((InvalidOperationException exception) =>
{
    if (exception.Message != "Invalid attempt to call CheckDataIsReady when reader is closed.")
    {
        return false;
    }

    GC.Collect();
    GC.WaitForFullGCComplete();
    return true;

})
.Run();

如果您对此有更好的解决方案,请告诉我