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

时间:2010-04-08 01:00:11

标签: 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.ExecuteXmlReader();

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

    return msg;
}

我无法关闭方法中的连接,否则将终止响应消息的流式传输。由于控制在完成该方法后返回到WCF系统,我不知道如何在之后关闭该连接。任何有关其他文档的建议或指示都将不胜感激。

3 个答案:

答案 0 :(得分:4)

好的问题,实际上。不幸的是,我相信没有一个好的答案。关于这个问题实际上有一个活跃的Microsoft Connect票。

通常情况下,如果您想要流式传输结果,而只需要定期SqlDataReader,则可以使用CommandBehavior重载,CommandBehavior.CloseConnection,特别是Close。如果使用此命令行为创建了阅读器,那么当您Dispose(或SqlConnection)阅读器时,它也会关闭基础连接,因此您永远不必担心处置ExecuteXmlReader

不幸的是,SqlConnection没有等效的重载。您必须明确处置XmlReader

解决此问题的一种方法是实现自己的XmlReader后代,包装从ExecuteXmlReader获取的真实XmlReader并强制关闭时关闭。

基本思路是从XmlReader派生并包装真实SqlConnectionclass 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; } } 方法和属性 - 这只是很多无聊的方法代理:

SqlDataReader

沉闷,沉闷,无光泽,但它有效。此阅读器将在完成后为您关闭连接,与使用CommandBehavior.CloseConnection打开的public static class SqlExtensions { public static XmlReader ExecuteSafeXmlReader(this SqlCommand cmd) { return new SqlXmlReader(cmd); } } 相同。

最后要做的是创建一个扩展方法,使其更易于使用:

XmlReader xr = cmd.ExecuteXmlReader();

一旦你有了这个,而不是写作:

XmlReader xr = cmd.ExecuteSafeXmlReader();

你写道:

{{1}}

就是这样。现在,当WCF关闭您的阅读器时,它将自动关闭底层连接。

(免责声明:这还没有经过正式测试,但我没有理由认为它不起作用,除非WCF实际上没有关闭阅读器。确保实际测试这个实时SQL连接以确保它确实没有泄漏连接。)

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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