Лучшие практики по совместному использованию IDbConnection или строки подключения / фабрики в вашем .Net коде - PullRequest
17 голосов
/ 05 января 2009

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

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

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

Спасибо

Ответы [ 4 ]

14 голосов
/ 05 января 2009

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

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

Мне не нравилось передавать объект 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.

Keith

2 голосов
/ 05 января 2009

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

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

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

1 голос
/ 22 декабря 2009

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

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

1 голос
/ 05 января 2009

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

...