Проверка объектов сущностей и проверка бизнес-правил - PullRequest
1 голос
/ 26 октября 2010

Я занимаюсь разработкой приложения с бизнес-объектами, созданными из EF 4.0. Приложение состоит из Data Layer, имеющего хранилище классов. Над этим находится бизнес-уровень, где бизнес-процессы отображаются в соответствии с требованиями. Бизнес-уровень вызывает уровень данных.

Бизнес-объекты имеют сложные ассоциации. Один сценарий: Торговец имеет несколько адресов Торговец принадлежит к одной категории У продавца есть аккаунт Аккаунт имеет кошелек

Пользователь будет создавать нового продавца вместе с вышеупомянутыми бизнес-объектами (график). Существуют различные бизнес-правила при создании и обновлении продавца.

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

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

Теперь, когда пользовательский интерфейс передает новый граф мерчанта компоненту бизнес-уровня, я сначала проверяю валидацию свойств скалярного BO (валидированные блоки валидации), затем этот BO передается в методы валидации правил бизнес-процессов для проверки различных правил. Существуют возможные точки нарушения, о которых я хочу сообщить, и которые не позволят сохранить объекты торговца и графика.

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

РЕДАКТИРОВАТЬ: я не хочу включать проверки в EF SaveChanges, ChangeAssociation и т. Д. Обработчики событий.

Ответы [ 2 ]

1 голос
/ 26 октября 2010

Хотя вы сказали, что не хотите генерировать исключения, я обнаружил, что выбрасывать исключения очень эффективно.Особенно, если у вас есть несколько подсистем, проверяющих систему, использование одного единственного типа исключения в качестве фасада будет очень эффективным.

Что вы можете сделать, это создать собственное исключение (то есть с именем ValidationException), которое будетгенерируется, когда ваш VAL-модуль сообщает об ошибке или бизнес-правила сообщают об ошибках.На уровне представления вы должны поймать это ValidationException и сообщить этот точный тип исключения удобным для пользователя способом.

Использование одного типа исключения для обеих подсистем проверки позволяет вамсообщать об ошибках согласованным образом, а также гарантирует, что ошибки проверки не останутся незамеченными, когда вы (или другой разработчик) забудете обработать ошибки проверки.Обработка ошибок должна быть очень явной ИМО.Когда вы позволяете методам возвращать список ошибок, их легко забыть обработать.При создании пользовательского исключения легко добавить свойство к этому типу исключения, которое содержит список ошибок проверки.Такой список ошибок легко извлекается из VAB.Я не знаю, какую систему проверки вы используете для проверки ваших бизнес-правил, но не так сложно извлечь из нее список кодов ошибок.

Самый простой способ справиться с этим в пользовательском интерфейсе - этоконечно, используя try - catch:

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

try
{
    businessCommand.Execute();
}
catch (ValidationException ex)
{
   UIValidationHelper.ReportValidationErrors(ex.Errors);
}

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

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

UIValidationHelper.ExecuteOrDisplayErrors(() => 
{
    businessCommand.Execute();
});

UIValidationHelper будет выглядеть так:

public static class UIValidationHelper
{
    public static void ExecuteOrDisplayErrors (Action action)
    {
        try
        {
           action();
        }
        catch (ValidationException ex)
        {
            // Show the errors in your UI technology
            ShowErrorMessage(ex.Errors);
        }
    }
}

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

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

businessCommand.ValidationErrorOccurred +=
    UIValidationHelper.DisplayValidationErrors;

businessCommand.Execute();

В этом примере статический метод подключается к событию ValidationErrorOccurred экземпляра команды.Хитрость заключается в том, чтобы позволить Execute методу этой команды перехватить ValidationException s и направить их в ValidationErrorOccurred, когда она будет зарегистрирована.Когда никакой метод не зарегистрирован, это исключение должно пузыриться в стеке вызовов, потому что необработанное исключение проверки, конечно, не должно остаться незамеченным.

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

При использовании веб-форм ASP.NET метод DisplayValidationErrors UIValidationHelper может выглядеть следующим образом:

public static class UIValidationHelper
{
    public static void DisplayValidationErrors(
        object sender, ValidationErrorEventArgs e)
    {
        Page page = GetValidPageFromCurrentHttpContext();
        var summary = GetValidationSummaryFromPage()

        foreach (var result in e.Error.Results)
        {
            summary.Controls.Add(new CustomValidator
            {
                ErrorMessage = result.Message,
                IsValid = false
            });
        }
    }

    private static Page GetValidPageFromCurrentHttpContext()
    {
        return (Page)HttpContext.Current.CurrentHandler;
    }

    private ValidationSummary GetValidationSummaryFromPage(Page page)
    {
        return page.Controls.OfType<ValidationSummary>().First();
    }
}

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

Последнее, что я хотел бы показать вам, - как будет выглядеть BusinessCommand при принятии этого решения.Базовый класс этих команд может выглядеть примерно так:

public abstract class BusinessCommand
{
    public event EventHandler<ValidationErrorEventArgs>
        ValidationErrorOccurred;

    public void Execute()
    {
        try
        {
            this.ExecuteInternal();
        }
        catch (ValidationException ex)
        {
            if (this.ValidationErrorOccurred != null)
            {
                var e = new ValidationErrorEventArgs(ex);
                this.ValidationErrorOccurred(this, e);
            }
            else
            {
                // Not rethrowing here would be a bad thing!
                throw;
            }
        }
    }

    protected abstract void ExecuteInternal();
}

ValidationErrorEventArgs выглядит так:

public class ValidationErrorEventArgs : EventArgs
{
    public ValidationErrorEventArgs(ValidationException error)
    {
        this.Error = error;
    }

    public ValidationException Error { get; private set; }
}

Я надеюсь, что все это имеет смысл, и извините за мой длинный ответ: -)

Удачи.

0 голосов
/ 26 октября 2010

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

Что касается части правил, я бы посмотрел шаблон посетителей для каждого графа объектов, который вы пытаетесь выполнить, проверяя некоторые правила. Я бы сообщил об этом, вероятно, как новый вложенный объект на основе того, что было найдено. Я лично предпочитаю эту сторону отчетности - использовать шаблон посетителя, который создает XML-документ или какой-либо другой пользовательский вложенный класс, в зависимости от ваших потребностей в эффективности. Фактические правила в шаблоне посетителя могут быть объявлены вне шаблона посетителя, предпочтительно в декларативном подходе. Например, CheckDuplicateRecord. Это позволило бы для повторного использования.

Сохраните все это на том же уровне, что и бизнес-уровень, но далее разделите бизнес-уровень на уровень проверки правил и бизнес-объекты.

Мой личный подход к использованию EF заключается в использовании объектов POCO, поскольку они обеспечивают масштабируемость. Затем я сделал бы некоторую проверку для пользовательского интерфейса, затем сделал бы некоторую проверку при переносе на уровень бизнес-объектов, а затем повторил бы то же самое на уровне EF DAL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...