C# 如何关闭用于在WCF服务中生成流结果的数据库连接?

C# 如何关闭用于在WCF服务中生成流结果的数据库连接?,c#,wcf,streaming,database-connection,C#,Wcf,Streaming,Database Connection,我找不到任何关于在WCF服务操作中正确关闭数据库连接的文档。我有一个通过以下方法返回流式响应的服务 public virtual Message GetData() { string sqlString = BuildSqlString(); SqlConnection conn = Utils.GetConnection(); SqlCommand cmd = new SqlCommand(sqlString, conn); XmlReader xr = cmd

我找不到任何关于在WCF服务操作中正确关闭数据库连接的文档。我有一个通过以下方法返回流式响应的服务

public virtual Message GetData()
{
    string sqlString = BuildSqlString();
    SqlConnection conn = Utils.GetConnection();
    SqlCommand cmd = new SqlCommand(sqlString, conn);
    XmlReader xr = cmd.ExecuteXmlReader();

    Message msg = Message.CreateMessage(
        OperationContext.Current.IncomingMessageVersion,
        GetResponseAction(),
        xr);

    return msg;
}

我无法关闭方法内的连接,否则响应消息流将被终止。由于控制在该方法完成后返回到WCF系统,因此我不知道以后如何关闭该连接。如果您有任何关于其他文档的建议或建议,我们将不胜感激。

实际上,这是个好问题。不幸的是,我认为没有好的答案。事实上,关于这个问题有一张有效的罚单

通常,如果您想要流式传输结果,并且只需要一个常规的
SqlDataReader
,则可以使用重载,该重载采用
CommandBehavior
,特别是
CommandBehavior.CloseConnection
。如果读卡器是使用此命令行为创建的,那么当您
关闭
(或
处理
)读卡器时,它也会关闭底层连接,因此您不必担心处理
SqlConnection

不幸的是,
ExecuteXmlReader
没有等效的重载。您必须显式地处理
SqlConnection

解决这个问题的一种方法是实现您自己的
XmlReader
子代,包装从
ExecuteXmlReader
获取的真实
XmlReader
,并强制关闭连接

其基本思想就是从
XmlReader
派生并包装真正的
XmlReader
SqlConnection
本身。大概是这样的:

class SqlXmlReader : XmlReader
{
    private SqlConnection connection;
    private XmlReader reader;

    public SqlXmlReader(SqlCommand cmd)
    {
        if (cmd == null)
            throw new ArgumentNullException("cmd");
        this.connection = cmd.Connection;
        this.reader = cmd.ExecuteXmlReader();
    }

    public override void Close()
    {
        reader.Close();
        connection.Close();
    }
}
这直接从
SqlCommand
获取连接和读卡器,因此不存在连接/读卡器不匹配的可能性。您还需要实现其余的
XmlReader
方法和属性-这只是一大堆枯燥的方法代理:

    public override int AttributeCount
    {
        get { return reader.AttributeCount; }
    }

    public override string BaseURI
    {
        get { return reader.BaseURI; }
    }

    public override int Depth
    {
        get { return reader.Depth; }
    }

    public override bool EOF
    {
        get { return reader.EOF; }
    }

    public override string GetAttribute(int i)
    {
        return reader.GetAttribute(i);
    }

    public override string GetAttribute(string name, string namespaceURI)
    {
        return reader.GetAttribute(name, namespaceURI);
    }

    public override string GetAttribute(string name)
    {
        return reader.GetAttribute(name);
    }

    public override bool HasValue
    {
        get { return reader.HasValue; }
    }

    public override bool IsEmptyElement
    {
        get { return reader.IsEmptyElement; }
    }

    public override string LocalName
    {
        get { return reader.LocalName; }
    }

    public override string LookupNamespace(string prefix)
    {
        return reader.LookupNamespace(prefix);
    }

    public override bool MoveToAttribute(string name, string ns)
    {
        return reader.MoveToAttribute(name, ns);
    }

    public override bool MoveToAttribute(string name)
    {
        return reader.MoveToAttribute(name);
    }

    public override bool MoveToElement()
    {
        return reader.MoveToElement();
    }

    public override bool MoveToFirstAttribute()
    {
        return reader.MoveToFirstAttribute();
    }

    public override bool MoveToNextAttribute()
    {
        return reader.MoveToNextAttribute();
    }

    public override XmlNameTable NameTable
    {
        get { return reader.NameTable; }
    }

    public override string NamespaceURI
    {
        get { return reader.NamespaceURI; }
    }

    public override XmlNodeType NodeType
    {
        get { return reader.NodeType; }
    }

    public override string Prefix
    {
        get { return reader.Prefix; }
    }

    public override bool Read()
    {
        return reader.Read();
    }

    public override bool ReadAttributeValue()
    {
        return reader.ReadAttributeValue();
    }

    public override ReadState ReadState
    {
        get { return reader.ReadState; }
    }

    public override void ResolveEntity()
    {
        reader.ResolveEntity();
    }

    public override string Value
    {
        get { return reader.Value; }
    }
枯燥,枯燥,枯燥,但它是有效的。完成连接后,此读卡器将为您关闭连接,与使用
CommandBehavior.CloseConnection
打开的
SqlDataReader
相同

最后一件事是创建一个扩展方法,使其更易于使用:

public static class SqlExtensions
{
    public static XmlReader ExecuteSafeXmlReader(this SqlCommand cmd)
    {
        return new SqlXmlReader(cmd);
    }
}
一旦你有了这个,就不要写了:

XmlReader xr = cmd.ExecuteXmlReader();
你写道:

XmlReader xr = cmd.ExecuteSafeXmlReader();
就这样。现在,当WCF关闭读卡器时,它将自动关闭底层连接


(免责声明:这尚未经过正式测试,但我看不出它不起作用的原因,除非WCF实际上没有关闭读卡器。请确保对live SQL连接进行实际测试,以确保它确实没有泄漏连接。)

您可以尝试某种形式的基于或的服务。这将允许您在一次调用中发出请求,并保持SqlConnection打开,直到发出
Disconnect()
样式的调用。此断开连接可能导致相关SQL对象的
.Dispose()

您可能会考虑让您的服务类实现IDispose

@Aaron:您不应该在该类上实现
IDisposable
。@John:base
XmlReader
已经实现了
IDisposable
,在该方法中调用虚拟的
Close
方法,因此在这种情况下,无需再次执行该操作。