MVC验证 - 使用服务层保持干燥 - 最佳做法是什么?

spo*_*f3r 15 c# validation model-view-controller asp.net-mvc asp.net-mvc-3

我正在尝试遵循最佳的多层设计实践,并且不希望我的MVC控制器与我的DAL(或任何IRepository)进行交互.它必须通过我的业务服务层来执行适当的业务规则和验证.验证 - 我不想在我的域模型实体上使用各种验证属性(例如[必需])在控制器中执行验证,因为这揭示了我的前端.更不用说这项服务也可以通过WPF前端实现.

由于我的验证是在我的服务层完成的,将值返回给UI的最佳做法是什么?我不想要'void addWhatever(int somethingsID)',因为我需要知道它是否失败了.它应该是一个布尔值吗?应该是Enum吗?我应该利用异常处理吗?或者,在将验证属性装饰到Model对象时,是否应该返回一些类似于MVC使用的IValidationDictionary对象?(如果需要,我可以在UI中使用适配器模式)

我想将我的实体从控制器传递到服务层,并了解验证/数据持久性是否失败.我也不想忽视这样一个事实,即我需要返回一个视图,指出可能验证失败的每个字段的正确错误消息(我希望尽可能保持无痛).

我有几个想法,所有这些想法都不对.我觉得答案包括特定于视图的模型实体,但这会导致必须处理的整个映射问题,更不用说这违反了DRY(不要重复自己)原则.什么是最佳做法?

Eri*_*sch 17

我知道,似乎做MVC验证违反DRY,但实际上......它不会......至少不适用于大多数(非平凡的)应用程序.

为什么?因为您的视图的验证要求通常与业务对象验证要求不同.您的视图验证涉及验证特定视图是否有效,而不是您的业务模型是有效的.

有时这两者是相同的,但如果您构建应用程序以使视图要求业务模型有效,那么您将自己锁定到此方案中.如果您需要将对象创建拆分为两个页面,会发生什么?如果您决定将服务层用于Web服务,会发生什么?通过将您的UI锁定到业务层验证方案中,您可以严重削弱您可以提供的各种解决方案.

视图是输入的验证,而不是模型的验证.


Sta*_*anK 6

这就是我做到的.

让您的服务层在业务规则/验证规则失败时抛出异常.为此创建自己的验证异常,并包含一些属性来保存验证错误的详细信息 - (例如,哪个属性具有验证错误,以及消息是什么)

然后在Exception上创建一个扩展方法,它将错误的细节复制到ModelState(我从Steve Sandersons那里获得了相当优秀的'Pro Asp.Net MVC 2 Framework'一书) - 如果你这样做,MVC将突出显示无效字段,在UI等中显示错误

然后你的控制器将包含这样的东西

try
{
    Service.DoSomeThing();
}
catch (Exception err)
{
    err.CopyTo(ModelState);
}
Run Code Online (Sandbox Code Playgroud)

这意味着您的业务规则和验证现在位于您的服务层中,并且可以重复使用.

考虑将DTO/View模型传递给您的视图,并将您的域对象映射到DTO和(反之亦然),而不是将您的域对象传递给您的视图.

然后DTO/View模型可以驻留在MVC层中,您可以使用Validation属性对它们进行装饰,并让控制器将这些属性传递给视图 - 从而使用内置的MVC验证.

您会发现,对于任何复杂项目,UI端所需的验证可能与业务规则结束时所需的验证略有不同,因此这有助于分离.

有一个很好的库叫做AutoMapper,它可以很容易地从你的域对象映射到你的DTO(反之亦然),而不需要很多样板代码.

  • 我同意,更多的是MVC层验证没有捕获的任何无效数据_is_例外,并且被serice层捕获.我实际上使用NHibernate.Validator作为事物的验证方面,它会在数据层中抛出异常.我在MVC层中也使用内置验证接口.想法是MVC层_should_使用内置数据注释捕获输入错误,但如果无效数据通过MVC层,服务层将抛出异常 (3认同)
  • 我喜欢这个,除了它只会通知客户第一次验证失败.如果请求中存在大量验证失败,则客户端必须一次修复一个,并在每次修复后重新发送整个请求. (3认同)
  • 嗯,我不确定无效数据是"特殊的",值得一个例外.为什么选择异常而不是返回结果或使用内置验证接口? (2认同)