pge*_*hev 4 c# database entity-framework
我有一个FileForUploading应该上传到数据库的类。
public class FileForUploading
{
public FileForUploading(string filename, Stream stream)
{
this.Filename = filename;
this.Stream = stream;
}
public string Filename { get; private set; }
public Stream Stream { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
我正在使用实体框架将其转换FileForUploadingEntity
为一个非常简单的类,但只包含该Filename属性。我不想将其存储Stream在内存中,而是将其直接上传到数据库。
将数据Stream直接“流式传输”到数据库的最佳方式是什么?
到目前为止,我想出了这个
private void UploadStream(string name, Stream stream)
{
var sqlQuery = @"UPDATE dbo.FilesForUpload SET Content =@content WHERE Name=@name;";
var nameParameter = new SqlParameter()
{
ParameterName = "@name",
Value = name
};
var contentParameter = new SqlParameter()
{
ParameterName = "@content",
Value = ConvertStream(stream),
SqlDbType = SqlDbType.Binary
};
// the database context used throughout the application.
this.context.Database.ExecuteSqlCommand(sqlQuery, contentParameter, nameParameter);
}
Run Code Online (Sandbox Code Playgroud)
这是 myConvertStream将 the 转换Stream为 a byte[]。(它作为 a 存储varbinary(MAX)在数据库中。
private static byte[] ConvertStream(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
Run Code Online (Sandbox Code Playgroud)
上述解决方案是否足够好?如果Stream它很大,它会表现好吗?
我不想将 Stream 存储在内存中,而是将其直接上传到数据库。
使用您提出的上述解决方案,您的应用程序中的内存中仍然有流的内容,您最初提到这是您试图避免的。
最好的办法是绕过 EF 并使用 async 函数上传流。以下示例取自MSDN 文章 SqlClient Streaming Support。
// Application transferring a large BLOB to SQL Server in .Net 4.5
private static async Task StreamBLOBToServer() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
using (SqlCommand cmd = new SqlCommand("INSERT INTO [BinaryStreams] (bindata) VALUES (@bindata)", conn)) {
using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) {
// Add a parameter which uses the FileStream we just opened
// Size is set to -1 to indicate "MAX"
cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file;
// Send the data to the server asynchronously
await cmd.ExecuteNonQueryAsync();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将此示例转换为以下内容以使其适合您。请注意,您应该更改方法上的签名以使其异步,这样您就可以利用在长期数据库更新期间不会阻塞线程的优势。
// change your signature to async so the thread can be released during the database update/insert act
private async Task UploadStreamAsync(string name, Stream stream) {
var conn = this.context.Database.Connection; // SqlConnection from your DbContext
if(conn.State != ConnectionState.Open)
await conn.OpenAsync();
using (SqlCommand cmd = new SqlCommand("UPDATE dbo.FilesForUpload SET Content =@content WHERE Name=@name;", conn)) {
cmd.Parameters.Add(new SqlParameter(){ParameterName = "@name",Value = name});
// Size is set to -1 to indicate "MAX"
cmd.Parameters.Add("@content", SqlDbType.Binary, -1).Value = stream;
// Send the data to the server asynchronously
await cmd.ExecuteNonQueryAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
再一记。如果您想保存大型非结构化数据集(即您上传的流),那么最好不要将它们保存在数据库中。有很多原因,但最重要的是,关系数据库的设计并没有真正考虑到这一点,处理数据很麻烦,而且它们可以真正快速地消耗数据库空间,使其他操作(即备份、恢复等)变得更加困难)。
有一种替代方法仍然允许您在本机中将指针保存在记录中,但将实际的非结构化数据驻留在磁盘上。您可以使用Sql Server FileStream执行此操作。在 ADO.NET 中,您将使用SqlFileStream。这是关于如何配置 Sql Server 和数据库以允许 Sql File Streams 的一个很好的演练。它还有一些关于如何使用SqlFileStream该类的Vb.net 示例。
我确实假设您使用 Microsoft Sql Server 作为您的数据存储库。如果此假设不正确,请更新您的问题,并为您正在连接的正确数据库服务添加标签。