gpt4 book ai didi

c# - 使用 SqlDataReader 时,如何使来自 BLOB 的流在普通旧 C# 对象中可用?

转载 作者:太空狗 更新时间:2023-10-29 17:51:16 25 4
gpt4 key购买 nike

场景是这样的:

  • 我们存储文件,例如相对较大的文档 (10-300MB),在我们的 MSSQL 数据库中以 blob 形式存在。
  • 我们有一个非常小的域模型,因此我们为我们的存储库使用干净的 SqlDataReader 方法,而不是 ORM,以避免不必要的依赖。
  • 我们想在 ASP.NET/ASP.NET MVC 网页上使用服务器上下文中的对象。
  • 我们不想暂时将 blob 存储在 byte[] 中,以避免服务器上的高内存使用

所以我一直在做的是实现我自己的 SqlBlobReader。它继承了 Stream 和 IDisposable,在实例化期间,我们必须提供一个包含查询的 SqlCommand,该查询返回一行和一列,当然,这是我们想要流式传输的 blob。然后我的 C# 域对象可以有一个 Stream 类型的属性,它返回一个 SqlBlobReader 实现。然后可以在流式传输到 ASP.net MVC 等中的 FileContentStream 时使用此流。

它将立即使用 SequentialAccess 执行 ExecuteReader 以启用来自 MSSQL 服务器的 blob 流。这意味着我们在使用它时必须小心尽快处理流,并且我们总是在需要时懒惰地实例化 SqlBlobReader,例如在我们的域对象中使用存储库调用。

那么我的问题是:

  • 当使用 SqlDataReader 而不是 ORM 时,这是在普通旧域对象上实现 blob 流的聪明方法吗?
  • 我不是 ADO.NET 专家,这个实现看起来合理吗?

SqlBlobReader.cs:

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;

namespace Foo
{
/// <summary>
/// There must be a SqlConnection that works inside the SqlCommand. Remember to dispose of the object after usage.
/// </summary>
public class SqlBlobReader : Stream
{
private readonly SqlCommand command;
private readonly SqlDataReader dataReader;
private bool disposed = false;
private long currentPosition = 0;

/// <summary>
/// Constructor
/// </summary>
/// <param name="command">The supplied <para>sqlCommand</para> must only have one field in select statement, or else the stream won't work. Select just one row, all others will be ignored.</param>
public SqlBlobReader(SqlCommand command)
{
if (command == null)
throw new ArgumentNullException("command");
if (command.Connection == null)
throw new ArgumentException("The internal Connection cannot be null", "command");
if (command.Connection.State != ConnectionState.Open)
throw new ArgumentException("The internal Connection must be opened", "command");
dataReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
dataReader.Read();
this.command = command; // only stored for disposal later
}

/// <summary>
/// Not supported
/// </summary>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}

/// <summary>
/// Not supported
/// </summary>
public override void SetLength(long value)
{
throw new NotSupportedException();
}

public override int Read(byte[] buffer, int index, int count)
{
long returned = dataReader.GetBytes(0, currentPosition, buffer, 0, buffer.Length);
currentPosition += returned;
return Convert.ToInt32(returned);
}

/// <summary>
/// Not supported
/// </summary>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}

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

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

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

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

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

protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (dataReader != null)
dataReader.Dispose();
SqlConnection conn = null;
if (command != null)
{
conn = command.Connection;
command.Dispose();
}
if (conn != null)
conn.Dispose();
disposed = true;
}
}
base.Dispose(disposing);
}

public override void Flush()
{
throw new NotSupportedException();
}

}

}

在 Repository.cs 中:

  public virtual Stream GetDocumentFileStream(int fileId)
{
var conn = new SqlConnection {ConnectionString = configuration.ConnectionString};
var cmd = new SqlCommand
{
CommandText =
"select DocumentFile " +
"from MyTable " +
"where Id = @Id",
Connection = conn,
};


cmd.Parameters.Add("@Id", SqlDbType.Int).Value = fileId;
conn.Open();
return new SqlBlobReader(cmd);
}

在 DocumentFile.cs 中:

  public Stream GetStream()
{
return repository.GetDocumentFileStream(Id);
}

在 DocumentController.cs 中:

  // A download controller in ASP.net MVC 2

[OutputCache(CacheProfile = "BigFile")]
public ActionResult Download(int id)
{
var document = repository.GetDocument(id);
return new FileStreamResult(document.DocumentFile.GetStream(), "application/pdf")
{
FileDownloadName = "Foo.pdf";
};
}

最佳答案

有一个错误;你忽略了用户的参数,你应该保护 -ve returned:

  public override int Read(byte[] buffer, int index, int count)
{
long returned = dataReader.GetBytes(0, currentPosition,
buffer, 0, buffer.Length);
currentPosition += returned;
return Convert.ToInt32(returned);
}

应该是:

  public override int Read(byte[] buffer, int index, int count)
{
long returned = dataReader.GetBytes(0, currentPosition,
buffer, index, count);
if(returned > 0) currentPosition += returned;
return (int)returned;
}

(否则你写入缓冲区的错误部分)

但总的来说看起来不错。

关于c# - 使用 SqlDataReader 时,如何使来自 BLOB 的流在普通旧 C# 对象中可用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4396863/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com