Sub Sonic не закрывает соединения - PullRequest
0 голосов
/ 12 ноября 2009

Я использую Linq из SubSonic 3 следующим образом:

for(int x; x < 100; x++) {
   var v = (from c in db.categories
            where c.parent == 10
            select c);

   if (v.Count() > 0) return null;

   category[] c = v.ToArray();
}

по какой-то причине SubSonic не закрывает соединения ... поэтому после нескольких запусков вышеуказанного цикла у меня заканчиваются соединения SQL в пуле, или MySQL просто отказывается разрешить больше соединений ... Я пробовал это оба с SS 3.0.3 и с SVN, и я продолжаю получать эти ошибки.

Что мне делать, чтобы закрыть соединения после получения набора результатов?

Спасибо

Ответы [ 2 ]

3 голосов
/ 31 декабря 2009

Кажется, есть некоторые проблемы с библиотекой MySQL .NET. Несколько дней назад они исправили некоторые из этих проблем с 6.2.2, связанные с освобождением соединений. Но есть и проблема с SubSonic. Я использовал шаблоны LINQ с MySQL для генерации своих классов. Всякий раз, когда FirstOrDefault () или First () (другие похожие функции, вероятно, имеют ту же проблему).

Для запроса, такого как:

var db = new MyDb("CONNECTIONSTRING_NAME");

var userExt = (from pe in db.PhoneExtensions
               where
                   pe.FirstName.ToLower() == firstName.ToLower() &&
                   pe.LastName.ToLower() == lastName.ToLower()
               select pe.Extension).FirstOrDefault();

Это приведет к выполнению запроса, и читатель будет не удален.

Проблема в Linq.Structure.DbQueryProvider в методе Project of T.

while (reader.Read())
{
    yield return fnProjector(reader);
}
reader.Dispose();

Dispose () никогда не вызывается при использовании FirstOrDefault () и других подобных методов.

Простое исправление:

try
{
    while (reader.Read())
    {
        yield return fnProjector(reader);
    }
}
finally
{
    reader.Dispose();
}

Простой быстрый тест, показывающий проблему:

private class DbDataReader : System.IDisposable
{
    #region IDisposable Members

    public void Dispose() { }

    #endregion
}
private class DbQueryProvider
{
    private DbDataReader _reader;

    public bool IsReaderDisposed { get { return _reader == null; } }

    public DbQueryProvider()
    {
        _reader = new DbDataReader();
    }

    public IEnumerable<int> Project(int numResults)
    {
        int i = 0;
        while (i < numResults)
        {
            yield return i++;
        }
        _reader.Dispose();
        _reader = null;
    }

    public IEnumerable<int> ProjectWithFinally(int numResults)
    {
        int i = 0;
        try
        {
            while (i < numResults)
            {
                yield return i++;
            }
        }
        finally
        {
            _reader.Dispose();
            _reader = null;
        }
    }

}

[Test]
public void YieldReturn_Returns_TrueForIsReaderDisposed()
{
    const int numResults = 1;

    var qp1 = new DbQueryProvider();
    var q1 = qp1.Project(numResults);
    Assert.IsInstanceOf(typeof(int), q1.First());

    var qp2 = new DbQueryProvider();
    var q2 = qp2.Project(numResults);
    Assert.IsInstanceOf(typeof(int), q2.FirstOrDefault());

    var qp3 = new DbQueryProvider();
    var q3 = qp3.Project(numResults);
    Assert.IsInstanceOf(typeof(int), q3.Single());

    var qp4 = new DbQueryProvider();
    var q4 = qp4.Project(numResults);
    Assert.IsInstanceOf(typeof(int), q4.SingleOrDefault());

    Assert.IsTrue(qp1.IsReaderDisposed);
    Assert.IsTrue(qp2.IsReaderDisposed);
    Assert.IsTrue(qp3.IsReaderDisposed);
    Assert.IsTrue(qp4.IsReaderDisposed);
}

[Test]
public void YieldReturnFinally_Returns_TrueForIsReaderDisposed()
{
    const int numResults = 1;

    var qp1 = new DbQueryProvider();
    var q1 = qp1.ProjectWithFinally(numResults);
    Assert.IsInstanceOf(typeof(int), q1.First());

    var qp2 = new DbQueryProvider();
    var q2 = qp2.ProjectWithFinally(numResults);
    Assert.IsInstanceOf(typeof(int), q2.FirstOrDefault());

    var qp3 = new DbQueryProvider();
    var q3 = qp3.ProjectWithFinally(numResults);
    Assert.IsInstanceOf(typeof(int), q3.Single());

    var qp4 = new DbQueryProvider();
    var q4 = qp4.ProjectWithFinally(numResults);
    Assert.IsInstanceOf(typeof(int), q4.SingleOrDefault());

    Assert.IsTrue(qp1.IsReaderDisposed);
    Assert.IsTrue(qp2.IsReaderDisposed);
    Assert.IsTrue(qp3.IsReaderDisposed);
    Assert.IsTrue(qp4.IsReaderDisposed);
}

YieldReturnFinally_Returns_TrueForIsReaderDisposed проходит, но YieldReturn_Returns_TrueForIsReaderDisposed завершается ошибкой.

Я проверил это на проекте, над которым я работаю, который скоро будет в производстве, и, похоже, это работает без проблем. Протестировано с максимальным размером пула соединений 5 и не имело проблем с пулом соединений (никогда не заканчивались соединения на моем компьютере разработчика при выполнении 1 запроса за раз). Я также обнаружил некоторые проблемы в Extensions.Database, связанные с изменением типов и назначений.

Я разработал проект на github, внес изменения и сделал запрос на извлечение информации, надеюсь, он попадет к нужным людям.

3 голосов
/ 13 ноября 2009

Проблема - хотите верьте, хотите нет - не в SubSonic. Это драйвер $ * $ & $ MySQL. Мы явно закрываем соединение, когда вы выполняете подобные запросы, но я видел, что драйвер MySQL полностью игнорирует закрытие в пользу некоторых действительно, очень неудачных попыток оптимизации.

Я не знаю, что вам здесь сказать - мне очень жаль говорить.

...