Улучшите Шаблон метода выбора уровня доступа к данным

В последнее время я пишу методы выбора уровня доступа к данным, где код все принимает эту общую форму:

public static DataTable GetSomeData( ... arguments)
{
    string sql = " ... sql string here:  often it's just a stored procedure name ... ";

    DataTable result = new DataTable();

    // GetOpenConnection() is a private method in the class: 
    // it manages the connection string and returns an open and ready connection
    using (SqlConnection cn = GetOpenConnection())
    using (SqlCommand cmd = new SqlCommand(sql, cn))
    {
        // could be any number of parameters, each with a different type
        cmd.Parameters.Add("@Param1", SqlDbType.VarChar, 50).Value = param1; //argument passed to function

        using (SqlDataReader rdr = cmd.ExecuteReader())
        {
            result.Load(rdr);
        }
    }

    return result;
}

Или как это:

public static DataRow GetSomeSingleRecord( ... arguments)
{
    string sql = " ... sql string here:  often it's just a stored procedure name ... ";

    DataTable dt = new DataTable();

    // GetOpenConnection() is a private method in the class: 
    // it manages the connection string and returns an open and ready connection
    using (SqlConnection cn = GetOpenConnection())
    using (SqlCommand cmd = new SqlCommand(sql, cn))
    {
        // could be any number of parameters, each with a different type
        cmd.Parameters.Add("@Param1", SqlDbType.VarChar, 50).Value = param1; //argument passed to function

        using (SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.SingleRow))
        {
            dt.Load(rdr);
        }
    }

    if (dt.Rows.Count > 0)
         return dt.Rows[0];
    return null;
}

Эти методы назвал бы бизнес-код слоя, который затем преобразовывает основной DataTable или DataRecord в бизнес-объекты со строгим контролем типов, которые может использовать уровень представления.

Так как я использую подобный код неоднократно, я хочу удостовериться, что этот код является лучшим, это может быть. Таким образом, как это может быть улучшено? И, это стоящий попытки переместить общий код от этого к своему собственному методу. Если так, на что тот метод был бы похож (конкретно относительно передачи набора SqlParameter в)?

8
задан Joel Coehoorn 26 July 2019 в 14:33
поделиться

5 ответов

Один шаблон, которым я наслаждался, похож на это насколько клиентский код идет:

        DataTable data = null;
        using (StoredProcedure proc = new StoredProcedure("MyProcName","[Connection]"))
        {
            proc.AddParameter("@LoginName", loginName);
            data = proc.ExecuteDataTable();
        }

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

Мне нравится это, потому что это легко считать и скрывает весь код ADO от меня.

2
ответ дан 5 December 2019 в 21:23
поделиться

Единственная вещь, которую я делаю по-другому, является мной переключенный от моих собственных внутренних вспомогательных методов базы данных до фактического Блока приложений Доступа к данным http://msdn.microsoft.com/en-us/library/cc309504.aspx

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

1
ответ дан 5 December 2019 в 21:23
поделиться

Существует столько способов реализовать DBAL, по-моему, Вы находитесь на правильном пути. Somethings для рассмотрения в реализации:

  • Вы используете подобный фабрике метод для создания SqlConnection, это - деталь, но можно сделать то же для SqlCommand.
  • Длина параметра является дополнительной, таким образом, можно на самом деле упустить ее из Параметра. Добавьте вызов.
  • Создайте методы для добавления параметров также, примера кода ниже.

Добавьте использование параметров DbUtil.AddParameter(cmd, "@Id", SqlDbType.UniqueIdentifier, Id);

internal class DbUtil {

internal static SqlParameter CreateSqlParameter(
    string parameterName,
    SqlDbType dbType,
    ParameterDirection direction,
    object value
) {
    SqlParameter parameter = new SqlParameter(parameterName, dbType);

    if (value == null) {
        value = DBNull.Value;
    }

    parameter.Value = value;

    parameter.Direction = direction;
    return parameter;
}

internal static SqlParameter AddParameter(
    SqlCommand sqlCommand,
    string parameterName,
    SqlDbType dbType
) {
    return AddParameter(sqlCommand, parameterName, dbType, null);
}

internal static SqlParameter AddParameter(
    SqlCommand sqlCommand,
    string parameterName,
    SqlDbType dbType,
    object value
) {
    return AddParameter(sqlCommand, parameterName, dbType, ParameterDirection.Input, value);
}

internal static SqlParameter AddParameter(
    SqlCommand sqlCommand,
    string parameterName,
    SqlDbType dbType,
    ParameterDirection direction,
    object value
) {
    SqlParameter parameter = CreateSqlParameter(parameterName, dbType, direction, value);
    sqlCommand.Parameters.Add(parameter);
    return parameter;
    }
}
1
ответ дан 5 December 2019 в 21:23
поделиться

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

Мои мысли о прокрутке Вашего собственного кода доступа к данным:

  • Со временем я нашел легче не иметь отдельные объекты DAL/BL, а скорее объединить их в отдельный объект (некоторое время спустя после того, чтобы сделать этот вывод, я узнал, что это - довольно известный шаблон - а именно, ActiveRecord). Это могло бы выглядеть хорошим и отделенным, чтобы иметь отдельные блоки DAL, но издержки в затратах на обслуживание сложат. Каждый раз Вы добавляете новую опцию, необходимо будет создать больше, кодируют/изменяют больше классов. По моему опыту, команда, которая поддерживает приложение, часто является путем меньше, чем исходная команда разработчиков, которые создали его, и они будут ненавидеть дополнительную требуемую работу.
  • Для многочисленных команд могло бы иметь смысл разделять DAL (и позволять коллективной работе на нем, в то время как другие. Но это делает хороший стимул для чрезмерного увеличения размера кода.
  • Приход к определенному образцу: как Вы используете получающийся DataTable? Выполните итерации строк, создайте типизированные объекты и получите данные из строки? Если ответ да, думайте о дополнительном DataTable, который Вы создали только для движущихся данных между DAL и BL. Почему бы не взять его непосредственно от DataReader?
  • Также об образце: если Вы возвращаете невведенный DataTable, то я предполагаю, что необходимо использовать имена столбцов (набора результатов возвраты вызова SP) путь в коде вызова. Это означает, должен ли я изменить что-то в базе данных, она могла бы влиять на оба слоя.

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

  • Сделайте базовый класс для своих введенных бизнес-объектов.
  • Сохраните объектное состояние в базовом классе (новым, измененным и т.д.)
  • Поместите основные методы доступа к данным в этот класс как статические методы. С небольшим усилием (подсказка: общие методы + Активатор. CreateInstance), можно создать один бизнес-объект на каждую строку, возвращенную в читателе.
  • сделайте абстрактный метод в бизнес-объекте для парсинга данных строки (непосредственно от DataReader!) и заливка объект.
  • сделайте статические методы в полученных бизнес-объектах, которые готовят сохраненные proc параметры (в зависимости от различных критериев фильтра) и называют универсальные методы доступа к данным от базового класса.

Цель состоит в том, чтобы закончиться с использованием, таким как:

List<MyObject> objects = MyObject.FindMyObject(string someParam);

Преимущество для меня было то, что я только должен изменить один файл для преодоления изменений в именах столбцов базы данных, типы и т.д. (небольшие изменения в целом). С некоторыми хорошо думал регионы, можно организовать код так, чтобы они были отдельными "слоями" в том же объекте :). Другое преимущество - то, что базовый класс является действительно допускающим повторное использование от одного проекта до другого. И чрезмерное увеличение размера кода минимально (хорошо, по сравнению с преимуществами. Вы могли также заполнить наборы данных и связать их со средствами управления UI :D

Ограничения - Вы заканчиваете с одним классом на объект области (обычно на основную таблицу базы данных). И Вы не можете загрузить объекты в существующих транзакциях (хотя Вы могли думать о передаче транзакции, если у Вас есть один).

Сообщите мне, интересно ли Вам более подробно - я мог бы развернуть ответ немного.

1
ответ дан 5 December 2019 в 21:23
поделиться

Пришлось добавить собственное:
Возврат DataReader из DataLayer в операторе Using

Новый шаблон позволяет мне иметь в памяти только одну запись за раз, но по-прежнему закрывает соединение в красивом операторе использования:

public IEnumerable<T> GetSomeData(string filter, Func<IDataRecord, T> factory)
{
    string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter";

    using (SqlConnection cn = new SqlConnection(GetConnectionString()))
    using (SqlCommand cmd = new SqlCommand(sql, cn))
    {
        cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter;
        cn.Open();

        using (IDataReader rdr = cmd.ExecuteReader())
        {
            while (rdr.Read())
            {
                yield return factory(rdr);
            }
            rdr.Close();
        }
    }
}
3
ответ дан 5 December 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: