Вопрос

У меня, видимо, есть два варианта:

  1. Сделать мой класс реализацией IDisposable.Создать мой DbCommand случаи как private readonly поля и в конструкторе добавьте параметры, которые они используют.Всякий раз, когда я хочу выполнить запись в базу данных, привязывайтесь к этим параметрам (повторно используя одни и те же экземпляры команд), устанавливайте Connection и Transaction свойства, затем позвоните ExecuteNonQueryDispose метод, вызов Dispose по каждому из этих полей.
  2. Каждый раз, когда я хочу выполнить запись в базу данных, напишите using(var cmd = new DbCommand("...", connection, transaction)) вокруг использования команды, а также добавлять параметры и привязываться к ним каждый раз перед вызовом ExecuteNonQuery.Я предполагаю, что мне не нужна новая команда для каждого запроса, просто новая команда каждый раз, когда я открываю базу данных (верно?).

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

Что касается № 1, моих пользователей раздражает то, что я теперь являюсь этим классом. IDisposable только потому, что я использовал несколько DbCommands (это должна быть деталь реализации, которая их не волнует).Я также несколько подозреваю, что сохранение DbCommand экземпляр вокруг может случайно заблокировать базу данных или что-то в этом роде?

Что касается № 2, мне кажется, что я выполняю много работы (с точки зрения объектов .NET) каждый раз, когда хочу выполнить запись в базу данных, особенно при добавлении параметров.Кажется, что я каждый раз создаю один и тот же объект, а это просто плохая практика.

Для справки, вот мой текущий код, использующий № 1:

using System;
using System.Net;
using System.Data.SQLite;

public class Class1 : IDisposable
{
    private readonly SQLiteCommand updateCookie = new SQLiteCommand("UPDATE moz_cookies SET value = @value, expiry = @expiry, isSecure = @isSecure, isHttpOnly = @isHttpOnly WHERE name = @name AND host = @host AND path = @path");
    public Class1()
    {
        this.updateCookie.Parameters.AddRange(new[]
                            {
                                new SQLiteParameter("@name"),
                                new SQLiteParameter("@value"),
                                new SQLiteParameter("@host"),
                                new SQLiteParameter("@path"),
                                new SQLiteParameter("@expiry"),
                                new SQLiteParameter("@isSecure"),
                                new SQLiteParameter("@isHttpOnly")
                            });
    }

    private static void BindDbCommandToMozillaCookie(DbCommand command, Cookie cookie)
    {
        long expiresSeconds = (long)cookie.Expires.TotalSeconds;

        command.Parameters["@name"].Value = cookie.Name;
        command.Parameters["@value"].Value = cookie.Value;
        command.Parameters["@host"].Value = cookie.Domain;
        command.Parameters["@path"].Value = cookie.Path;
        command.Parameters["@expiry"].Value = expiresSeconds;
        command.Parameters["@isSecure"].Value = cookie.Secure;
        command.Parameters["@isHttpOnly"].Value = cookie.HttpOnly;
    }

    public void WriteCurrentCookiesToMozillaBasedBrowserSqlite(string databaseFilename)
    {
        using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + databaseFilename))
        {
            connection.Open();
            using (SQLiteTransaction transaction = connection.BeginTransaction())
            {
                this.updateCookie.Connection = connection;
                this.updateCookie.Transaction = transaction;

                foreach (Cookie cookie in SomeOtherClass.GetCookieArray())
                {
                    Class1.BindDbCommandToMozillaCookie(this.updateCookie, cookie);
                    this.updateCookie.ExecuteNonQuery();
                }

                transaction.Commit();
            }
        }
    }

    #region IDisposable implementation
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed && disposing)
        {
            this.updateCookie.Dispose();
        }
        this.disposed = true;
    }
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
    ~Class1()
    {
        this.Dispose(false);
    }

    private bool disposed;
    #endregion
}
Это было полезно?

Решение

Доменик, Удаление SQLiteCommand просто сигнализирует активному считывателю о необходимости удалить его считыватель и устанавливает параметры и ссылку на соединение в null.

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

Таким образом, повторное использование кэшированной команды и простое присвоение значений параметрам на сегодняшний день является наиболее эффективной реализацией.

.Prepare() — это noop в SQLiteCommand, поэтому никакой пользы от этого не будет.

Другие советы

Не существует единственного «правильного способа» управления жизненным циклом объекта базы данных.Все зависит от потребностей вашего приложения.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top