Как создать объекты передачи данных в бизнес-логическом слое

DTO

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

Я использую LLBLGen для создания уровня доступа к данным (с использованием SQL Server 2008). Цель состоит в том, чтобы построить уровень бизнес-логики, который защищает веб-приложение от деталей DAL и, конечно же, обеспечивает дополнительный уровень проверки за пределами DAL. Кроме того, насколько я могу судить сейчас, веб-сервис будет по существу тонкой оболочкой над BLL.

DAL, конечно, имеет свой собственный набор объектов сущности, например CustomerEntity, ProductEntity и т.д. Однако я не хочу, чтобы уровень представления имел доступ к этим объектам напрямую, поскольку они содержат специальные методы DAL, а сборка специфична для DAL и так далее. Таким образом, идея состоит в создании объектов передачи данных (DTO). Идея состоит в том, что это, по сути, простые старые объекты С#/.NET, которые имеют все поля, скажем, CustomerEntity, которые на самом деле являются таблицей базы данных Customer, но ни один другой материал, за исключением, может быть, некоторых свойств IsChanged/IsDirty. Таким образом, были бы CustomerDTO, ProductDTO и т.д. Я предполагаю, что они наследуют базовый класс DTO. Я считаю, что могу сгенерировать их с помощью какого-то шаблона для LLBLGen, но пока не уверен.

Итак, идея состоит в том, что BLL будет раскрывать свою функциональность, принимая и возвращая эти объекты DTO. Я думаю, что веб-служба будет обрабатывать преобразование этих объектов в XML для сторонних пользователей, использующих его, многие из них могут не использовать .NET(также некоторые вещи будут script, вызываемые из вызовов AJAX в веб-приложении, используя JSON).

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

1) Как это должно быть показано клиентам (уровень представления и код веб-службы)

Я думал, что будет один открытый класс, который имеет эти методы, каждый вызов будет атомарной операцией:

InsertDTO, UpdateDTO, DeleteDTO, GetProducts, GetProductByCustomer и т.д....

Затем клиенты просто вызывают эти методы и передают соответствующие аргументы, обычно DTO.

Это хороший, работоспособный подход?

2) Что вернуть из этих методов? Очевидно, методы Get/Fetch возвратят DTO. Но как насчет вложений? Часть подписи может быть:

InsertDTO(DTO dto)

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

Один из вариантов, о котором я думал, - это класс Result:

class Result
{
    public Exception Error {get; set;}
    public DTO AffectedObject {get; set;}
}

Итак, на вставке DTO получит свой идентификатор get ID (например, CustomerDTO.CustomerID) и затем помещает в этот объект результата. Клиент будет знать, есть ли ошибка, если Result.Error!= Null, а затем он будет знать идентификатор из свойства Result.AffectedObject.

Это хороший подход? Одна из проблем заключается в том, что кажется, что он передает много данных назад и вперед, что является избыточным (когда это только идентификатор). Я не думаю, что добавление свойства "int NewID" будет чистым, потому что некоторые вставки не будут иметь такой автоинкрементный ключ. Другая проблема заключается в том, что я не думаю, что веб-службы справятся с этим хорошо? Я считаю, что они просто вернут базовый DTO для AffectedObject в классе Result, а не в производный DTO. Я предполагаю, что могу решить это, имея много разных объектов Result (возможно, полученных из базового Result и наследующего свойство Error), но это не кажется очень чистым.

Хорошо, я надеюсь, что это не слишком многословно, но я хочу быть ясным.

Ответ 1

1: Это довольно стандартный подход, который хорошо подходит для реализации "репозитория" для наилучшего подхода, который можно выполнить с точки зрения тестирования.

2: Исключения (которые должны быть объявлены как "ошибки" на границе WCF, кстати) будут автоматически подняты. Вам не нужно обращаться с этим напрямую. Для данных - существует три общих подхода:

  • используйте ref в контракте (не очень красиво)
  • возвращает объект (обновленный) - т.е. public DTO SomeOperation(DTO item);
  • возвращает только обновленную идентификационную информацию (первичный ключ /timestamp/etc )

Все дело в том, что для каждой операции не требуется другой тип (сравните свой класс Result, который нужно будет дублировать на DTO).

Ответ 2

Q1: Вы можете думать о своих составных типах Контента данных WCF как DTO для решения этой проблемы. Таким образом, ваш слой пользовательского интерфейса имеет доступ только к свойствам DataContract DataMember. Ваши атомарные операции будут методами, открытыми вашим интерфейсом WCF.

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