Как закрыть соединение с базой данных, используемое для получения результата потоковой передачи в службе WCF? - PullRequest
3 голосов
/ 08 апреля 2010

Мне не удалось найти документацию по правильному закрытию соединений с базой данных в операциях службы 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 ]

4 голосов
/ 08 апреля 2010

Хороший вопрос, на самом деле. К сожалению, один, на который я считаю, нет хорошего ответа. На самом деле есть активная Microsoft Connect заявка на эту проблему.

Обычно, если вы хотите транслировать результаты и вам просто нужна обычная SqlDataReader, вы можете использовать перегрузку ExecuteReader, которая принимает 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();

Вот и все. Теперь, когда WCF закроет ваш ридер, он автоматически закроет основное соединение.

(Отказ от ответственности: это официально не проверялось, но я не вижу причин, почему это не сработало бы, если WCF фактически не закрывает читателя. Обязательно проверьте это на работающем соединении SQL, чтобы убедиться, что что он действительно не пропускает соединения.)

1 голос
/ 08 апреля 2010

Возможно, вы захотите, чтобы ваш класс обслуживания реализовал IDispose

1 голос
/ 08 апреля 2010

Вы можете попробовать какую-либо форму службы Duplex или Session . Это позволит вам сделать запрос за один вызов и держать SqlConnection открытым, пока не будет выполнен вызов в стиле Disconnect(). Это отключение может .Dispose() связанных объектов SQL.

...