我无法找到有关在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系统,我不知道如何在之后关闭该连接。任何有关其他文档的建议或指示都将不胜感激。
答案 0 :(得分:4)
好的问题,实际上。不幸的是,我相信没有一个好的答案。关于这个问题实际上有一个活跃的Microsoft Connect票。
通常情况下,如果您想要流式传输结果,而只需要定期SqlDataReader
,则可以使用CommandBehavior
重载,CommandBehavior.CloseConnection
,特别是Close
。如果使用此命令行为创建了阅读器,那么当您Dispose
(或SqlConnection
)阅读器时,它也会关闭基础连接,因此您永远不必担心处置ExecuteXmlReader
不幸的是,SqlConnection
没有等效的重载。您必须明确处置XmlReader
。
解决此问题的一种方法是实现自己的XmlReader
后代,包装从ExecuteXmlReader
获取的真实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; }
}
方法和属性 - 这只是很多无聊的方法代理:
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 Service或Session的服务。这将允许您在一次调用中发出请求并保持SqlConnection打开,直到进行Disconnect()
样式调用。此断开连接可以.Dispose()
相关的SQL对象。
答案 2 :(得分:1)
您可能会考虑让您的服务类实现IDispose