Обобщенный метод заполнения DTO с различными списками выбора запросов - PullRequest
0 голосов
/ 05 мая 2019

По причинам, которые я не совсем понимаю, я решил не использовать ORM Framework и перешел на обобщенный уровень доступа к данным ADO.NET.Первоначально я создал один класс базы данных, из которого все мои контроллеры имели доступ.Как мог предсказать любой, кроме меня, этот объект доступа стал чудовищным.

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

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

public class ReferenceDataService : IReferenceDataService
{
    private IDatabaseAdapter _dbAdapter;

    public ReferenceDataService(IDatabaseAdapter dbAdapter)
    {
        _dbAdapter = dbAdapter;
    }

    public IEnumerable<ReferenceData> GetReferenceData(string table)
    {
        List<ReferenceData> rdList = new List<ReferenceData>();

        StringBuilder sb = new StringBuilder();
        sb.Append("SELECT [CODE], [LABEL] FROM [dbo].");
        sb.Append(table);
        sb.Append(" WHERE END_DATETIME > GETDATE()");

        DataSet ds = _dbAdapter.ExecuteDataSet(sb.ToString(), null);

        foreach (DataRow row in ds.Tables[0].Rows)
        {
            rdList.Add(PopulateRecord(row));
        }

        return rdList;
    }

    private ReferenceData PopulateRecord(DataRow row)
    {
        return new ReferenceData
        {
            ReferenceId = (int)row["REFERENCE_ID"],
            Code = (string)row["CODE"],
            Label = (string)row["LABEL"],
            Description = (string)row["DESCRIPTION"],
            BeginDatetime = (DateTime)row["BEGIN_DATETIME"],
            EndDatetime = (DateTime)row["END_DATETIME"],

            UpdatedBy = (string)row["UPDATED_BY"],
            UpdatedOn = (DateTime)row["UPDATED_ON"],
            CreatedBy = (string)row["CREATED_BY"],
            CreatedOn = (DateTime)row["CREATED_ON"]
        };
    }
}

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

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

1 Ответ

0 голосов
/ 16 мая 2019

После некоторого копания кажется, что было очень очевидное и простое решение, которое я упустил.Объект экземпляра DataRow может проверять наличие столбцов родительской таблицы.Обернув каждое назначение из строки таблицы в одну из этих проверок, метод заполненности не будет заботиться о том, что фактически было выбрано в DataTable, и сможет заполнять объект независимо от объема данных, возвращаемых из запроса.

Таким образом, в моем примере, если я хочу сохранить универсальный метод заполнения для ReferenceData, но использовать запрос, который только повторяет столбцы CODE и LABEL, следующее изменение позволит сохранить заполненность возвращенного бизнес-объекта независимой от ошибок:

    private ReferenceData PopulateRecord(DataRow row)
    {
        return new ReferenceData
        {
            ReferenceId = row.Table.Columns.Contains("REFERENCE_ID") ? (int)row["REFERENCE_ID"] : default(int),
            Code = row.Table.Columns.Contains("CODE") ? (string)row["CODE"] : default(string),
            Label = row.Table.Columns.Contains("LABEL") ? (string)row["LABEL"] : default(string),
            Description = row.Table.Columns.Contains("DESCRIPTION") ? (string)row["DESCRIPTION"] : default(string),
            BeginDatetime = row.Table.Columns.Contains("BEGIN_DATETIME") ? (DateTime)row["BEGIN_DATETIME"] : default(DateTime),
            EndDatetime = row.Table.Columns.Contains("END_DATETIME") ? (DateTime)row["END_DATETIME"] : default(DateTime),

            UpdatedBy = row.Table.Columns.Contains("UPDATED_BY") ? (string)row["UPDATED_BY"] : default(string),
            UpdatedOn = row.Table.Columns.Contains("UPDATED_ON") ? (DateTime)row["UPDATED_ON"] : default(DateTime),
            CreatedBy = row.Table.Columns.Contains("CREATED_BY") ? (string)row["CREATED_BY"] : default(string),
            CreatedOn = row.Table.Columns.Contains("CREATED_ON") ? (DateTime)row["CREATED_ON"] : default(DateTime)
        };
    }

Это позволило бы мне использовать PopulateRecord в операторе выбора, который возвращал только CODE и LABEL (как я хотел бы сделать, если бы я заполнял SelectItemList для раскрывающегося списка, например).

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

Если есть более эффективные способы решения этой проблемы, пожалуйста, дайте мне знать.Спасибо!

...