通过LINQ从SQL加载400多个图像

时间:2011-01-30 10:05:46

标签: sql-server linq

我遇到了通过Linq同时加载400多张图片的问题。

所有图像都以二进制形式存储在数据库中(无需讨论)。

要检索它们,我会提取唯一ID并将其发送到浏览器。 我有一个.ashx处理程序,它接收ID并将图像作为jpeg-data返回。

所以在标记中我只是把

<img id="img" src='/GetWebImage.ashx?id=<%= Model.Id %>' />

这引起了一些问题,因为当我尝试在SQL服务器squirks时加载400+时。挂钩Fiddler我可以看到SQL服务器在大部分图像上返回404,错误如下:

ExecuteReader需要一个开放且可用的连接。连接的当前状态是连接。

已经有一个与此命令关联的打开DataReader,必须先关闭

读取器关闭时调用Read的尝试无效

所以我猜测sql server无法一次处理所有的调用。

有人可以帮我正确地做到这一点吗?

我需要在一次调用中从数据库加载400多个图像。我可以加载图像(而不仅仅是ID,并通过模型解析,我将如何从二进制System.Drawing.Image转到-tag中的路径?

另一个解决方案是逐个加载图像jQuery(并等待加载图像)然后将它们插入到正确的地方 - 但这似乎是一种非常糟糕的方法。所以我猜我从一开始就做错了。

更新

感谢您提出的好建议。我发现我的处理程序实际上使用GDS缩放每个图像。不这样做,使得处理程序更快地服务于图像而不会失败。所以我猜SQL Server问题实际上是由.net无法足够快地扩展引起的。 我有点困惑,因为我有另一项服务以相同的方式提供10.000+图像,没有任何问题。

但我肯定会去一些缓存,因为每张图片只有一个唯一的URL。 有关缓存的任何建议吗?

对于包含许多图像的未来项目,我将用于存储图像的路径并将其保存在文件系统中。然后IIS将是服务图像的限制。

4 个答案:

答案 0 :(得分:2)

你应该真的使用缓存来做这种事情。

答案 1 :(得分:1)

对于缓存,只需将此代码添加到您的处理程序:

Response.Cache.SetCacheability(HttpCacheability.Public);

答案 2 :(得分:0)

您可以查看

  

MARS(多个有效结果集)MARS

在连接字符串中使用“Asynchronous Processing = true”。 希望这会有所帮助。

答案 3 :(得分:0)

为避免大图像或多次调用DB的内存不足异常,请使用以下代码对图像进行流式处理:

 /// <summary>
 /// Writes the binary data at the specified data record index to a stream using a buffer.
 /// </summary>
 /// <param name="source">The source.</param>
 /// <param name="stream">The stream to write to.</param>
 /// <param name="index">The index in the data record to read from.</param>
 /// <remarks>
 /// The data is read sequentially.
 /// </remarks>
 public static void WriteToStream(IQueryable<Binary> source, Stream stream, int index)
 {
     var cmd = DbContext.GetCommand(source);
     cmd.Connection.Open();
     try
     {
         using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
         {
             while (reader.Read())
             {
                 using (var recordStream = new DataRecordStream(reader, index))
                     recordStream.CopyTo(stream);
            }
         }
     }
     finally
     {
         cmd.Connection.Close();
     }
 }

这是DataRecordStream类,它包装了一个dataReader并使其行为像一个流:

public class DataRecordStream : Stream
{
    #region Fields

    private long _position;
    private bool _isOpen; 

    #endregion

    #region Properties

    public override bool CanRead
    {
        get { return _isOpen; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public IDataRecord DataRecord { get; private set; }

    public int Index { get; private set; } 

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="DataRecordStream"/> class.
    /// </summary>
    /// <param name="dataRecord">The data record.</param>
    /// <param name="index">The index in the data record to read from.</param>
    public DataRecordStream(IDataRecord dataRecord, int index)
    {
        DataRecord = dataRecord;
        Index = index;
        _position = 0;
        _isOpen = true;
    } 

    #endregion

    #region Functions

    public override void Flush()
    {
    }

    public override long Length
    {
        get { throw new NotSupportedException(); }
    }

    public override long Position
    {
        get
        {
            return _position;
        }
        set
        {
            throw new NotSupportedException();
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        _isOpen = false;
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (!_isOpen)
            throw new ObjectDisposedException("Stream is closed");

        int bytesRead = (int)DataRecord.GetBytes(Index, _position, buffer, offset, count);
        _position += bytesRead;
        return bytesRead;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    } 

    #endregion