处理N层,域驱动设计,MVC应用程序中的异常

时间:2011-03-17 01:44:44

标签: c# exception-handling domain-driven-design

我已经构建了一个如下所示的MVC应用程序:

MVC Application - Application Layer - Business Layer - Repository - Data

我已经阅读了many个应用程序的代码,并发现没有人似乎以类似于传递数据的方式传递异常。

换句话说,数据通过接口引用并在不同的上下文中重用。如果数据层中发生异常,它将如何发送到GUI或相关层?应该使用接口吗?在this应用程序中处理异常的构造是什么?

1 个答案:

答案 0 :(得分:5)

我没有读过你链接的doco但是我有一个简单的例子,我提出异常以回应不满足不变量。假设我正在创建一个实例,由于某种原因它是“无效的”,假设用户输入不好。而不是让我的实体处于无效状态(在DDD中是禁止的)并让系统“验证”它,它会在创建时抛出异常。相关的“验证消息”(对于用户)是从不满足的同一规范实例中提取的,我的派生异常包含UI所需的值。

InvariantException的示例:

public class InvariantException : MyAppException
{
    public object FailingObject = null;
    public ModelStateDictionary ModelState = new ModelStateDictionary();


    public InvariantException() { }

    public InvariantException(object failingObject, ModelStateDictionary messages)
    {
        this.FailingObject = failingObject;
        this.ModelState = messages;
    }

    public InvariantException(object failingObject, ModelStateDictionary messages,
        Exception innerException)
        : base("refer to ModelState", innerException)
    {
        this.FailingObject = failingObject;
        this.ModelState = messages;
    }
}

为用户/用户界面返回相关“验证消息”的规范示例:

public class PostFieldLengthSpecification : ISpecification<Post>
{
    private  const string TITLE_LENGTH_RANGE = "5-100";
    private  const string BODY_LENGTH_RANGE = "20-10000";


    public bool IsSatisfiedBy(Post post)
    {
        return this.GetErrors(post).IsValid;
    }


    public ModelStateDictionary GetErrors(Post post)
    {
        ModelStateDictionary modelState = new ModelStateDictionary();

        if (!post.Title.Trim().Length.Within(TITLE_LENGTH_RANGE))
            modelState.AddModelError(StongTypeHelpers.GetPropertyName((Post p) => p.Title),
                "Please make sure the title is between {0} characters in length".With(TITLE_LENGTH_RANGE));

        if (!post.BodyMarkup.Trim().Length.Within(BODY_LENGTH_RANGE))
            modelState.AddModelError(StongTypeHelpers.GetPropertyName((Post p) => p.BodyMarkup),
                "Please make sure the post is between {0} characters in length".With(BODY_LENGTH_RANGE));

        return modelState;
    }
}

工厂如何永远不会创建无效实例的示例,而是抛出异常并为UI存放消息:

    public static Post GetNewPost(string title, string bodyMarkup, DateTime posted)
    {
        var post = new Post(0, title, bodyMarkup, posted, new List<Comment>());
        var fieldLengthSpec = new PostFieldLengthSpecification();

        if (fieldLengthSpec.IsSatisfiedBy(post))
            return post;
        else
            throw new InvariantException(post, fieldLengthSpec.GetErrors(post));
    }

最后,一个自定义模型绑定器的示例,用于捕获所述异常并将“无效对象”传递回具有错误消息的操作:

public class PostModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(Post))
        {
            try
            {
                // Create Post
                return base.BindModel(controllerContext, bindingContext);
            }
            catch (InvariantException ie)
            {
                // If invalid, add errors from factory to ModelState
                bindingContext.ModelState.AddNewErrors(ie.ModelState);
                bindingContext.ModelState.AddValuesFor<Post>(bindingContext.ValueProvider);
                return ie.FailingObject;
            }
        }

希望这有帮助。