Подтвердить что ты не робот

Рекомендации по обмену IDbConnection или строке подключения /factory в вашем .Net-коде

Мне интересно, какая будет лучшая точка в отношении основных соединений с базой данных в .NET-приложении (ADO.NET, но я думаю, что практика должна быть одинаковой для любого уровня данных). Должен ли я создавать соединение с базой данных и распространять его по всему моему приложению, или было бы лучше просто передать строки/фабрики соединений и создать соединение ad-hoc, когда это необходимо.

Поскольку я понимаю, что удар производительности не значим с пулом, и он позволяет мне легко восстановиться из сломанных подключений (просто будет создано новое соединение), но опять-таки объект соединения - хорошая, относительно высокоуровневая абстракция и создание новое соединение для каждой операции (а не команда SQL, но приложение) генерирует дополнительный дублированный код и кажется пустой тратой времени/ресурсов (?).

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

Спасибо

4b9b3361

Ответ 1

Мне было нужно пройти вокруг объекта соединения, чтобы я мог позволить нескольким бизнес-объектам сохранять себя в базе данных внутри одной транзакции.

Если каждый бизнес-объект должен был создать свой собственный SQLConnection в базе данных, транзакция будет эскалироваться в распределенную транзакцию, и я хотел бы избежать этого.

Мне не понравилось передавать объект SQLConnection в качестве параметра для сохранения объекта, поэтому я создал ConnectionManager, который обрабатывает для меня объект SQLConnection, отслеживая использование объекта SQLConnection и отключая объект SQLConnection, когда не в использовании.

Вот пример кода ConnectionManager:

public class ConnectionManager: IDisposable
{
    private ConnectionManager instance;

    [ThreadStatic]
    private static object lockObject; 
    private static Object LockObject
    {
        get
        {
            if (lockObject == null)
                lockObject = new object();
            return lockObject;
        }
    }

    [ThreadStatic]
    private static Dictionary<string, ConnectionManager> managers;
    private static Dictionary<string, ConnectionManager> Managers
    {
        get
        {
            if (managers == null)
                managers = new Dictionary<string, ConnectionManager>();
            return managers;
        }
    }

    private SqlConnection connection = null;
    private int referenceCount;
    private string name;


    public static ConnectionManager GetManager(string connectionName)
    {
        lock (LockObject)
        {
            ConnectionManager mgr;
            if (Managers.ContainsKey(connectionName))
            {
                mgr = Managers[connectionName];
            }
            else
            {
                mgr = new ConnectionManager(connectionName);
                Managers.Add(connectionName, mgr);
            }

            mgr.AddRef();
            return mgr;
        }
    }

    private ConnectionManager(string connectionName)
    {
        name = connectionName;
        connection = new SqlConnection(GetConnectionString(connectionName));
        connection.Open();
    }

    private string GetConnectionString(string connectionName)
    {
        string conString = Configuration.ConnectionString;
        return conString; 
    }

    public SqlConnection Connection
    {
        get { return connection; }
    }

    private void AddRef()
    {
        referenceCount += 1;
    }

    private void DeRef()
    {
        lock (LockObject)
        {
            referenceCount -= 1;
            if (referenceCount == 0)
            {
                connection.Dispose();
                Managers.Remove(name);
            }
        }
    }

#region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            DeRef();
        }
    }

    ~ConnectionManager()
    {
        Dispose(false);
    }

#endregion

}

Вот как я буду использовать его из бизнес-объекта:

public void Save()
{   
    using (ConnectionManager mrg = ConnectionManager.GetManager("SQLConnectionString")
    {
        using (SQLCommand cmd = new SQLCommand)
        {
            cmd.connection = mgr.Connection
            // More ADO Code Here
        }

        _childObject.Save(); //this child object follows the same pattern with a using ConnectionManager.
    }
}

Я сохраняю бизнес-объект, и все его дочерние элементы также сохраняются с использованием одного и того же объекта соединения. Когда область отпадает от исходного родителя, оператор using закрывает соединение.

Это образец, который я узнал от Рокки Лхотки в его структуре CSLA.

Кит

Ответ 2

Поставщик ADO.NET SQL Server сам объединяет соединение. Вы можете управлять размером пула MinPoolSize и MaxPoolSize в строке подключения.

Ответ 3

Вы действительно не должны справляться с этой проблемой самостоятельно, так как есть множество инструментов, которые могут сделать это для вас.

Если вы действительно хотите сделать это самостоятельно, загляните в шаблон Unit of Work, где вы можете управлять жизненным циклом соединения/транзакции. Вы, конечно, не хотите пытаться перемещаться по грязным водам, где открываются/закрываются соединения в разных местах.

Если вы решите, что ваши компоненты напрямую откроют подключения к db, вполне вероятно, что жизненный цикл соединения будет слишком тонким и приведет к множеству открытых/закрытых подключений для одной пользовательской операции.

Ответ 4

Одна вещь, чтобы быть осторожными в вашем примере, что приложения ASP.NET не должны использовать ThreadStatic хранения, так как нить может быть использована повторно, и если вы не очистить все объекты, которые вы в конечном итоге с соединительным подвешивания вокруг.

В приложении ASP.NET вместо этого я использую HttpContext.Items. Вы реализуете IDisposable, но я видел сценарии, в которых разработчики забывают вызвать Dispose или поместить код в используемый блок.