3

我正在从旧的 ASP.NET Web 窗体方式转换为 ASP.NET MVC。我有一个正在处理的项目,该项目在数据库中有大约 40-50 个表。我决定使用实体框架作为我的数据访问层。我还决定在 EF 上放置一个存储库层和工作单元抽象,这样我就不会被它束缚,这样我就可以进行单元测试。最后,我想让我的控制器“瘦”,所以我正在考虑为我的业务逻辑实现一个业务“服务”层。

我正在努力解决的问题是如何将业务逻辑错误从我的服务层传播到我的 Presentation UI 层,以便显示适当的错误?请注意,我正在尝试寻找不是 MVC 特定的解决方案,因为此服务/业务逻辑层可能会用于除 MVC 应用程序(控制台应用程序、Web 服务等)之外的其他事物

关于一些代码...

可以说我有一个像这样的 POCO / 数据 / 域模型:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsActive { get; set; }

    // other properties (navigation, etc)...
}

一个实体框架流利的配置/映射类,如下所示:

public class CategoryMap : EntityTypeConfiguration<Category>
{
    public CategoryMap()
    {
        this.HasKey(c => c.Id);
        this.Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); // auto increment identity in our DB schema
        this.Property(c=> c.Name)
            .IsRequired() // defined as NOT NULL in DB schema so we put a constraint here
            .HasMaxLength(150); // defined as varchar(150) in DB schema so we put a constraint here
        this.Property(c=> c.Description)
            .IsRequired(); // defined as NOT NULL in DB schema so we put a constraint here

        // fluent config for related entities (navigation properties) would go here...
    }
}

一个封装多个存储库的工作单元,如下所示:

public class UnitOfWork : IUnitOfWork
{
    private readonly MyDbContext context;
    private CategoryRepository catRepo;

    public UnitOfWork()
    {
         this.context = new MyDbContext();
    }

    public ICategoryRepository Categories
    {
        get { return this.catRepo?? (this.catRepo= new CategoryRepository (this.context)); }
    }
}

像这样的服务/业务逻辑层:

public class CategoryService : ICategoryService
{
    private readonly IUnitOfWork unitOfWork;
    public CategoryService(IUnitOfWork uow) // injected by IoC
    {
          this.unitOfWork = uow;
    }

    public Category CreateNewCategory(Category category)
    {
          if (category == null)
          {
              throw new ArgumentNullException("category cannot be null");
          }

          // Simple business logic here to make sure another category with this name does not already exist.
          int count = this.unitOfWork.Categories.Count(cat => cat.Name == category.Name);
          if (count > 0)
          {
              // *** This is the error I want the user to see in the UI ***
              throw new Exception("Sorry - a category with that name already exists!");
          }
    }
}

像这样的控制器:

public ManageCategoriesController : Controller
{
    ICategoryService catSvc;
    public ManageCategoriesController(ICategoryService svc) // injected by IoC
    {
        this.catSvc = svc;
    }


    [HttpPost]
    public ActionResult(CategoryCreateModel createModel) // my View Models / Create Models have Data Annotations on them
    {
        if (ModelState.IsValid)
        {
             // use of AutoMapper to map from View Model to domain model...
             Category cat = Mapper.Map<CategoryCreateModel , Category>(createModel);
             this.catSvc.CreateNewCategory(cat); // ***need to get potential errors from Service and display on form.***
             return this.RedirectToAction("Index");
        }
    }
}

首先,谁能告诉我我是否在使用视图模型的正确轨道上?我觉得每个域模型几乎都有三个视图模型(创建、编辑、视图/列表)。

其次,我的 EF 配置/映射类负责数据库约束。其中一些约束(例如最大长度)也是视图模型中的数据注释,可以很容易地显示在 UI 上。但是我在哪里可以显示我的自定义业务逻辑错误?

4

2 回答 2

0

首先,您对 MVC 的总体方法对我来说看起来不错 :-)

其次,您很可能希望在视图模型上使用 DataAnnotation 进行模型验证。查看这篇博客文章,了解如何在 ASP.MVC 中使用它。

如果自定义验证不适合数据注释,您可以在控制器中执行以下操作:

try
{
    // the following exception could be thown by some nested validation logic
    // e.g. while processing a post request
    throw new ValidationException("the error description");
}
catch (ValidationException exception)
{
    ModelState.AddModelError("", exception.Message);
}
于 2013-08-06T14:52:24.887 回答
0

这是一个相当古老的问题,但对于未来的读者,我想补充一些东西。

如果您实际上使用的是 N 层模式,那么实体验证应该在您的服务层中。不在您的 MVC 控制器中。

正确的做法是在模型类中使用 ValidationAttributes 进行基本模型验证,但在服务层中重新验证实体。在控制器中添加自定义异常处理,以捕获从服务层引发的任何验证错误,并显示错误消息。

如果您的服务层只是用来调用您的存储库,那么您做错了什么;)

于 2014-04-07T08:05:01.730 回答