首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用SqlFileStream从WCF服务返回流

使用SqlFileStream从WCF服务返回流
EN

Stack Overflow用户
提问于 2011-09-19 11:06:56
回答 2查看 6.2K关注 0票数 15

我有一个WCF服务,用户可以从该服务请求大型数据文件(存储在启用FileStream的SQL数据库中)。这些文件应该是流的,在发送之前不应该加载到内存中。

因此,我有以下方法,它应该返回一个由WCF服务调用的流,这样它就可以将stream返回给客户端。

代码语言:javascript
复制
public static Stream GetData(string tableName, string columnName, string primaryKeyName, Guid primaryKey)
    {
        string sqlQuery =
            String.Format(
                "SELECT {0}.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM {1} WHERE {2} = @primaryKey", columnName, tableName, primaryKeyName);

        SqlFileStream stream;

        using (TransactionScope transactionScope = new TransactionScope())
        {
            byte[] serverTransactionContext;
            string serverPath;
            using (SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnString"].ToString()))
            {
                sqlConnection.Open();

                using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection))
                {
                    sqlCommand.Parameters.Add("@primaryKey", SqlDbType.UniqueIdentifier).Value = primaryKey;

                    using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
                    {
                        sqlDataReader.Read();
                        serverPath = sqlDataReader.GetSqlString(0).Value;
                        serverTransactionContext = sqlDataReader.GetSqlBinary(1).Value;
                        sqlDataReader.Close();
                    }
                }
            }

            stream = new SqlFileStream(serverPath, serverTransactionContext, FileAccess.Read);
            transactionScope.Complete();
        }

        return stream;
    }

我的问题是TransactionScope和SqlConnection。我现在这样做不起作用,我得到一个TransactionAbortedException,上面写着“事务已中止”。在返回流之前,我可以关闭事务和连接吗?任何帮助都很感谢,谢谢。

编辑:

我为一个SqlFileStream创建了一个包装器,它实现了IDisposable,这样我就可以在流被释放后关闭所有东西。看上去很正常

代码语言:javascript
复制
public class WcfStream : Stream
{
    private readonly SqlConnection sqlConnection;
    private readonly SqlDataReader sqlDataReader;
    private readonly SqlTransaction sqlTransaction;
    private readonly SqlFileStream sqlFileStream;

    public WcfStream(string connectionString, string columnName, string tableName, string primaryKeyName, Guid primaryKey)
    {
        string sqlQuery =
            String.Format(
                "SELECT {0}.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM {1} WHERE {2} = @primaryKey",
                columnName, tableName, primaryKeyName);

        sqlConnection = new SqlConnection(connectionString);
        sqlConnection.Open();

        sqlTransaction = sqlConnection.BeginTransaction();

        using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection, sqlTransaction))
        {
            sqlCommand.Parameters.Add("@primaryKey", SqlDbType.UniqueIdentifier).Value = primaryKey;
            sqlDataReader = sqlCommand.ExecuteReader();
        }

        sqlDataReader.Read();

        string serverPath = sqlDataReader.GetSqlString(0).Value;
        byte[] serverTransactionContext = sqlDataReader.GetSqlBinary(1).Value;

        sqlFileStream = new SqlFileStream(serverPath, serverTransactionContext, FileAccess.Read);
    }

    protected override void Dispose(bool disposing)
    {
        sqlDataReader.Close();
        sqlFileStream.Close();
        sqlConnection.Close();
    }

    public override void Flush()
    {
        sqlFileStream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return sqlFileStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        sqlFileStream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return sqlFileStream.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        sqlFileStream.Write(buffer, offset, count);
    }

    public override bool CanRead
    {
        get { return sqlFileStream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return sqlFileStream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return sqlFileStream.CanWrite; }
    }

    public override long Length
    {
        get { return sqlFileStream.Length; }
    }

    public override long Position
    {
        get { return sqlFileStream.Position; }
        set { sqlFileStream.Position = value; }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-09-19 11:13:21

通常,我可能建议将流包装在一个自定义流中,该流在处理时关闭事务,但是IIRC WCF不能保证哪些线程执行什么,但是TransactionScope是特定于线程的。因此,更好的选择可能是将数据复制到MemoryStream中(如果不是太大的话)并返回该数据。4.0中的Stream.Copy方法应该很容易实现,但是要记住在最终的return (.Position = 0)之前将内存流倒转。

很明显,如果溪流很大,这将是个大问题.但是,如果流足够大,足以引起关注,那么我个人会担心它在TransactionScope中运行,因为它有内置的时间限制,并导致可序列化的隔离(默认情况下)。

最后一个建议是使用不依赖线程的SqlTransaction;您可以编写位于SqlFileStream周围的Stream包装器,并关闭Dispose()中的读取器、事务和连接(以及包装的流)。WCF在处理结果后将调用它(通过Close())。

票数 9
EN

Stack Overflow用户

发布于 2013-06-05 11:56:30

嗯,我可能在这里遗漏了一些东西,但在我看来,一种更简单的方法是将流提供给WCF方法并从那里写入,而不是试图返回客户端读取的流?

下面是WCF方法的一个示例:

代码语言:javascript
复制
public void WriteFileToStream(FetchFileArgs args, Stream outputStream)
{
    using (SqlConnection conn = CreateOpenConnection())
    using (SqlTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted))
    using (SqlCommand cmd = conn.CreateCommand())
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "usp_file";
        cmd.Transaction = tran;
        cmd.Parameters.Add("@FileId", SqlDbType.NVarChar).Value = args.Id;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            if (reader.Read())
            {
                string path = reader.GetString(3);
                byte[] streamContext = reader.GetSqlBytes(4).Buffer;

                using (var sqlStream = new SqlFileStream(path, streamContext, FileAccess.Read))
                    sqlStream.CopyTo(outputStream);
            }
        }

        tran.Commit();
    }
}

在我的应用程序中,使用者恰好是一个ASP.NET应用程序,调用代码如下所示:

代码语言:javascript
复制
_fileStorageProvider.WriteFileToStream(fileId, Response.OutputStream);
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7469955

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档