System.OutOfMemoryException – 当Entity Framework查询Varbinary类型的过大数据时

我正在尝试查询包含文件(1,2 Gb)的varbinary列。

我正在使用entity framework。 见下文:

要测试的数据库

 CREATE TABLE [dbo].[BIGDATA] ( [id] [bigint] IDENTITY(1,1) NOT NULL, [BIGDATA] [varbinary](max) NULL, CONSTRAINT [PK_BIGDATA] PRIMARY KEY CLUSTERED ([id] ASC) ) ON [PRIMARY] 

要测试的数据(任何1 Gb的文件)

 INSERT INTO [dbo].[BIGDATA]([BIGDATA]) VALUES ((SELECT BulkColumn FROM OPENROWSET(BULK N'C:\BigTest.txt', SINGLE_BLOB) AS Document)) 

控制器下载文件

 public FileResult Download() { try { var context = new Models.ELOGTESTEEntities(); var idArquivo = Convert.ToInt32(1); // The problem is here, when trying send command to SQL Server to read register var arquivo = (from item in context.BIGDATA where item.id.Equals(idArquivo) select item).Single(); var mimeType = ".txt"; byte[] bytes = System.Text.Encoding.GetEncoding("iso-8859-8").GetBytes("BigTest.txt"); return File(arquivo.BIGDATA1, mimeType, System.Text.Encoding.UTF8.GetString(bytes)); } catch (Exception ex) { throw ex; } } 

我可以使用Select * From BigData在SQL Server上正常查询。

但是,在Entity Framework(或带有ADO的命令)中,我得到了以下exception:

为System.OutOfMemoryException

有人知道如何解决这个问题吗?

哇这是很多数据。 我真的认为你不需要使用EF来获取这些数据,而是使用好的’ol SqlDataReader。

鉴于你的.net 4.0限制,我找到了一个自定义的实现,从大量varbinary列流式传输读取。 除了查看代码并确保其中没有.net 4.5快捷方式之外,我不能为此付出任何荣誉:

http://www.syntaxwarriors.com/2013/stream-varbinary-data-to-and-from-mssql-using-csharp/

修改 – 让我知道是否应该将这样的内容复制/粘贴到答案中,因为原始URL可能不是持久的。

编辑:如果URL消失,以下是链接中的代码:

用法:

 // reading and returning data to the client VarbinaryStream filestream = new VarbinaryStream( DbContext.Database.Connection.ConnectionString, "FileContents", "Content", "ID", (int)filepost.ID, true); // Do what you want with the stream here. 

代码:

 public class VarbinaryStream : Stream { private SqlConnection _Connection; private string _TableName; private string _BinaryColumn; private string _KeyColumn; private int _KeyValue; private long _Offset; private SqlDataReader _SQLReader; private long _SQLReadPosition; private bool _AllowedToRead = false; public VarbinaryStream( string ConnectionString, string TableName, string BinaryColumn, string KeyColumn, int KeyValue, bool AllowRead = false) { // create own connection with the connection string. _Connection = new SqlConnection(ConnectionString); _TableName = TableName; _BinaryColumn = BinaryColumn; _KeyColumn = KeyColumn; _KeyValue = KeyValue; // only query the database for a result if we are going to be reading, otherwise skip. _AllowedToRead = AllowRead; if (_AllowedToRead == true) { try { if (_Connection.State != ConnectionState.Open) _Connection.Open(); SqlCommand cmd = new SqlCommand( @"SELECT TOP 1 [" + _BinaryColumn + @"] FROM [dbo].[" + _TableName + @"] WHERE [" + _KeyColumn + "] = @id", _Connection); cmd.Parameters.Add(new SqlParameter("@id", _KeyValue)); _SQLReader = cmd.ExecuteReader( CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.CloseConnection); _SQLReader.Read(); } catch (Exception e) { // log errors here } } } // this method will be called as part of the Stream ímplementation when we try to write to our VarbinaryStream class. public override void Write(byte[] buffer, int index, int count) { try { if (_Connection.State != ConnectionState.Open) _Connection.Open(); if (_Offset == 0) { // for the first write we just send the bytes to the Column SqlCommand cmd = new SqlCommand( @"UPDATE [dbo].[" + _TableName + @"] SET [" + _BinaryColumn + @"] = @firstchunk WHERE [" + _KeyColumn + "] = @id", _Connection); cmd.Parameters.Add(new SqlParameter("@firstchunk", buffer)); cmd.Parameters.Add(new SqlParameter("@id", _KeyValue)); cmd.ExecuteNonQuery(); _Offset = count; } else { // for all updates after the first one we use the TSQL command .WRITE() to append the data in the database SqlCommand cmd = new SqlCommand( @"UPDATE [dbo].[" + _TableName + @"] SET [" + _BinaryColumn + @"].WRITE(@chunk, NULL, @length) WHERE [" + _KeyColumn + "] = @id", _Connection); cmd.Parameters.Add(new SqlParameter("@chunk", buffer)); cmd.Parameters.Add(new SqlParameter("@length", count)); cmd.Parameters.Add(new SqlParameter("@id", _KeyValue)); cmd.ExecuteNonQuery(); _Offset += count; } } catch (Exception e) { // log errors here } } // this method will be called as part of the Stream ímplementation when we try to read from our VarbinaryStream class. public override int Read(byte[] buffer, int offset, int count) { try { long bytesRead = _SQLReader.GetBytes(0, _SQLReadPosition, buffer, offset, count); _SQLReadPosition += bytesRead; return (int)bytesRead; } catch (Exception e) { // log errors here } return -1; } public override bool CanRead { get { return _AllowedToRead; } } #region unimplemented methods public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override void Flush() { throw new NotImplementedException(); } public override long Length { get { throw new NotImplementedException(); } } public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); } public override void SetLength(long value) { throw new NotImplementedException(); } #endregion unimplemented methods } 

尝试使用EF“AsNoTracking()”选项加载数据!

示例:MyContext.MyTable.AsNoTracking()。其中​​(x => x …..)

看来entity framework不支持将数据流式传输到varbinary字段。

你有几个选择。

  • 下载到ADO.NET进行大型传输。
  • 将数据库切换到FileStream而不是varbinary

编辑:假设您使用的是.NET 4.5,则应使用SqlDataReader.GetStream 。 这将允许流式传输文件,而无需将整个内容加载到内存中。