DataTable Wrapper или Как отделить пользовательский интерфейс от бизнес-логики

Я использую веб-формы, C #, Asp.net. Как мы все знаем, в этой модели пользовательский интерфейс и бизнес-логика часто смешаны. Как эффективно их разделить?

Я хотел бы использовать следующий пример: у меня есть GridView и DataTable (GridView привязывается к DataTable, а DataTable загружается из хранимой процедуры).

Я бы хотел, чтобы GridView (UI) и DataTable (бизнес-логика) были разделены.

Стоит ли писать обертку для DataTable? Существуют ли проверенные и проверенные практические примеры, которым вы могли бы порекомендовать следовать?

Если бы кто-то с опытом мог пролить свет, это было бы здорово. И в заключение я хотел бы сказать, что ASP MVC сейчас не подходит, поэтому не рекомендую его.

Уровень доступа к моей базе данных возвращает DataTable. Обратите внимание, что я ДОЛЖЕН использовать этот уровень базы данных, так как это политика компании.


person sarsnake    schedule 26.03.2009    source источник


Ответы (3)


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

Вы можете увидеть мой прогресс здесь и здесь.

На мой взгляд, A DataTable не представляет бизнес-логику. В частности, это данные, извлеченные непосредственно из базы данных. Бизнес-логика превращает эти данные в действительно полезный бизнес-объект.

Итак, первый шаг - отделить DataTable от бизнес-объекта.

Вы можете сделать это, создав объекты и List<object>, которые составляют таблицы данных и коллекции таблиц данных, а затем вы можете создать ListView, который отображает эти объекты. Я описываю последние шаги в ссылках, которые я разместил выше. И первые шаги так же просты, как следующие:

  1. Создайте класс, который будет представлять ваш объект.
  2. выполнить итерацию через свой DataTable (или DataSet, или как бы вы ни извлекали данные) и вставить эти поля в свойства этого объекта (или того List<T>);
  3. верните этот список в Gridview или ListView для отображения.

Таким образом, ваш ListView или Gridview не будут тесно связаны с методом, которым вы извлекаете свои данные. Что произойдет, если вы позже решите получить данные из запроса JSON или XML-файла? Тогда вам придется встроить это туда.

Шаг 1 - Получение данных из базы данных

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

Диаграмма классов

Foo
----
id
Name
Description

А вот метод:

 private void FillDefault(SqlDataReader reader, Foos foo)
        {
            try
            {
                foo.id = Convert.ToInt32(reader[Foo.Properties.ID]);
                foo.Name = reader[Foo.Properties.NAME].ToString();
                    
                
             if (!string.IsNullOrEmpty(
                reader[Foo.Properties.DESCRIPTION].ToString()))
                 foo.Description = 
                 reader[Foo.Properties.DESCRIPTION].ToString();
             else foo.Description = string.Empty;
            }
            catch (Exception ex)
            {
               throw new Exception(
               string.Format("Invalid Query. 
               Column '{0}' does not exist in SqlDataReader.", 
               ex.Message));
            }
        }

Как только это произойдет, вы можете вернуть список, выполнив этот процесс в цикле while, который нацелен на функцию SQLDataReader.Read().

Как только вы это сделаете, давайте представим, что возвращаемый вами Foo является списком. Если вы сделаете это и перейдете по первой ссылке, которую я дал выше, вы можете заменить Dictionary<TKey, TValue> на List<T> и получить тот же результат (с небольшими отличиями). Класс Properties просто содержит имена столбцов в базе данных, поэтому у вас есть одно место для их изменения (на случай, если вам интересно).

DataTable - обновление на основе комментария

Вы всегда можете вставить промежуточный объект. В этом случае я бы вставил бизнес-уровень между DataTable и пользовательским интерфейсом и обсудил, что буду делать выше. Но DataTable - это не бизнес-объект; это визуальное представление базы данных. Вы не можете перенести это на уровень пользовательского интерфейса и называть его разъединенным. Они говорят, что вам нужно использовать DataTable, они говорят, что вы должны перенести этот DataTable в пользовательский интерфейс? Я не могу представить, чтобы они это сделали. Если вы это сделаете, то вас никогда не расстанут. Вам всегда понадобится промежуточный объект между DataTable и уровнем пользовательского интерфейса.

person George Stocker    schedule 26.03.2009
comment
У меня проблемы с переходом по вашим ссылкам. Это может быть связано с тем, что ваши ситуации представляют собой определенные части процесса, в то время как для меня это первый раз, и я хотел бы начать с самого начала, а не со словаря. - person sarsnake; 26.03.2009
comment
В порядке; справедливо. Я могу расширить свой ответ, если хотите. - person George Stocker; 26.03.2009
comment
Я исправлюсь. Я ДОЛЖЕН использовать объект DataTable, поскольку это то, что возвращает мой уровень данных, что является политикой компании - мы все должны использовать этот класс доступа к данным. Как мне тогда развязать? - person sarsnake; 26.03.2009
comment
спасибо, хммм, в моем случае я думаю, мне придется преобразовать DataTable в список ‹T›. На этом этапе мне нужно будет спросить, в чем преимущество преобразования объекта, созданного для GridView, в общий список? - person sarsnake; 26.03.2009
comment
Я думаю, что в моем сценарии создание класса, наследуемого от DataTable, более применимо. Затем я могу управлять им как ответом на события пользовательского интерфейса. На самом деле, может быть, и нет - это почти то, чем я сейчас занимаюсь. Я не уверен, стоит ли отделяться, учитывая, что у меня есть ограничение на использование DataTable. - person sarsnake; 26.03.2009
comment
нет, мне не нужно переносить DataTable на уровень пользовательского интерфейса. Мне нужно использовать dataTable при получении данных (через уровень доступа к данным). Итак, следующим шагом после получения данных будет перенос данных в объект бизнес-уровня (например, List ‹t›, словарь или что-то еще)? - person sarsnake; 26.03.2009
comment
Если вы сохраняете данные как datatable (или класс, производный от Datatable), вы всегда будете привязаны к вашей текущей реализации. Что произойдет, если вы добавите столбцы в базу данных и теперь должны их удалить (это становится непросто). Что произойдет, если они изменят способ получения этих данных? - person George Stocker; 27.03.2009
comment
Преимущество использования List ‹T› или бизнес-объекта заключается в том, что вы по-прежнему используете DataTable для получения информации из базы данных, но если это изменится позже, вам нужно будет внести изменения только на уровне доступа к данным, а не в как ваш DAL, так и ваш слой пользовательского интерфейса. - person George Stocker; 27.03.2009
comment
Горток, еще один вопрос по этому поводу. Теперь я сохраняю таблицу данных внутри сеанса btwn postbacks. После разделения я все равно должен загружать List ‹T› в сеанс и обратно внутри пользовательского интерфейса, верно? Насколько мне известно, бизнес-уровень не должен иметь доступ к объекту Sessions. Просьба уточнить. - person sarsnake; 06.04.2009

Я бы начал с разделения таблицы данных прямо в корзину. Создайте уровень домена, а затем какой-нибудь уровень доступа к данным, который имеет дело с БД (рекомендуется ORM).

Затем создайте уровень обслуживания, который предоставляет данные пользовательскому интерфейсу. Вся бизнес-логика должна быть внутри службы или самих сущностей.

person JoshBerke    schedule 26.03.2009
comment
не уходи за мной. Я ДОЛЖЕН использовать объект DataTable, поскольку это то, что возвращает мой уровень данных, что является политикой компании - мы все должны использовать этот класс доступа к данным. - person sarsnake; 26.03.2009

Рассмотрите возможность реализации шаблона MVP (презентатор представления модели). Это дает вам разделение бизнес-логики через интерфейс докладчика, что также позволяет улучшить возможности модульного тестирования. Тогда ваш код страницы aspx является просто соединителем событий и получателем / установщиком свойств. Вы можете найти его в MS шаблонах и практиках блоков корпоративных приложений (CAB - составной блок приложения - если я не ошибаюсь).
Подробнее об этом можно прочитать здесь: http://msdn.microsoft.com/en-us/magazine/cc188690.aspx
Но также из DataTable / Наборы данных в объекты (POCO) является предпочтительным.

person Hrvoje Hudo    schedule 26.03.2009