use*_*830 9 c# asp.net-mvc dependency-injection castle-windsor
我使用的方法类似于ASP.NET MVC教程中的方法,您将控制器ModelState
集合的包装器传递给验证类,以便控制器可以访问错误信息.
这是一个熟练的例子:
interface IProductValidator {
void Validate(Product item);
}
class ProductValidator {
// constructor
public ProductValidator(ModelStateWrapper validationDictionary) { }
}
interface IProductService {
void AddProduct();
}
public class ProductService : IProductService {
// constructor
public ProductService(IProductValidator validator) { }
}
Run Code Online (Sandbox Code Playgroud)
使用Castle Windsor容器进行IoC/DI,如何创建IProductService
?通常,我会:
MvcApplication.IocContainer.Resolve<IProductService>()
Run Code Online (Sandbox Code Playgroud)
但是这不能将Controller的ModelState
属性值注入ProductValidator的构造函数中.我可以使用构造函数参数来连接它,但这看起来真的很难看.
我假设您希望传入的模型状态自动将任何错误注入您的模型中?恕我直言,ModelState 应该保持在原来的位置,并且您将验证错误带入其中。下面以我处理错误的方式为例。我并不是说这是最好的方法或唯一的方法,但这是验证层不必了解谁或什么消耗了验证错误的一种方法。
首先,在我的 poco 中,我使用System.ComponentModel.DataAnnotations
验证规则。例如,这是我的帐户类别。
public class Account : CoreObjectBase<Account>
{
public virtual int AccountId { get; set; }
[Required(ErrorMessage = "Email address is required.")]
public virtual string EmailAddress { get; set; }
[Required(ErrorMessage = "A password is required.")]
public virtual string Password { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
因为我希望能够自己启动验证(在 MVC 之外自行执行),所以我必须实现自己的验证器。
public class Validator<T> where T : CoreObjectBase<T>
{
public ValidationResponse Validate(T entity)
{
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(entity, null, null);
var isValid = Validator.TryValidateObject(entity, context, validationResults);
return new ValidationResponse(validationResults.ToArray());
}
}
Run Code Online (Sandbox Code Playgroud)
这是我传回的 ValidationResult
[Serializable]
public class ValidationResponse
{
public IList<ValidationResult> Violations { get; private set; }
public IList<ErrorInfo> Errors { get; private set; }
public bool HasViolations
{
get { return Violations.Count > 0; }
}
public ValidationResponse(params ValidationResult[] violations)
{
Violations = new List<ValidationResult>(violations);
var errors = from v in Violations
from n in v.MemberNames
select new ErrorInfo(n, v.ErrorMessage);
Errors = errors.ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
ErrorInfo 是一个非常基本的类,包含有关我的错误的信息
[Serializable]
public class ErrorInfo
{
public string ErrorMessage { get; private set; }
public object Object { get; private set; }
public string PropertyName { get; private set; }
public ErrorInfo(string propertyName, string errorMessage)
: this(propertyName, errorMessage, null)
{
}
public ErrorInfo(string propertyName, string errorMessage, object onObject)
{
PropertyName = propertyName;
ErrorMessage = errorMessage;
Object = onObject;
}
}
Run Code Online (Sandbox Code Playgroud)
为了用我的 poco 类很好地包装这个验证,我继承了一个基类。为了验证使其通用,继承的子级必须告诉基类它的类型。感觉是圆形的,但确实有效。
[Serializable]
public class CoreObjectBase<T> : IValidatable where T : CoreObjectBase<T>
{
#region IValidatable Members
public virtual bool IsValid
{
get
{
// First, check rules that always apply to this type
var result = new Validator<T>().Validate((T)this);
// return false if any violations occurred
return !result.HasViolations;
}
}
public virtual ValidationResponse ValidationResults
{
get
{
var result = new Validator<T>().Validate((T)this);
return result;
}
}
public virtual void Validate()
{
// First, check rules that always apply to this type
var result = new Validator<T>().Validate((T)this);
// throw error if any violations were detected
if (result.HasViolations)
throw new RulesException(result.Errors);
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
最后,如您所见,我的验证抛出了 RulesException。此类是所有错误的包装。
[Serializable]
public class RulesException : Exception
{
public IEnumerable<ErrorInfo> Errors { get; private set; }
public RulesException(IEnumerable<ErrorInfo> errors)
{
Errors = errors != null ? errors : new List<ErrorInfo>();
}
public RulesException(string propertyName, string errorMessage) :
this(propertyName, errorMessage, null)
{
}
public RulesException(string propertyName, string errorMessage, object onObject) :
this (new ErrorInfo[] { new ErrorInfo(propertyName, errorMessage, onObject) } )
{
}
}
Run Code Online (Sandbox Code Playgroud)
所以,话虽如此,我在控制器中的验证看起来更像这样
public ActionResult MyAction()
{
try
{
//call validation here
}
catch (RulesException ex)
{
ModelState.AddModelStateErrors(ex);
}
return View();
}
Run Code Online (Sandbox Code Playgroud)
ModelState.AddModelStateErrors(ex); 是我写的一个扩展方法。这很简单。
public static void AddModelStateErrors(this System.Web.Mvc.ModelStateDictionary modelState, RulesException exception)
{
foreach (ErrorInfo info in exception.Errors)
{
modelState.AddModelError(info.PropertyName, info.ErrorMessage);
}
}
Run Code Online (Sandbox Code Playgroud)
这样,我仍然可以对我的服务/存储库使用 DI,并让它们在我的模型无效时抛出错误。然后我让前端(无论是 MVC 应用程序、Web 服务还是 Windows 应用程序)决定如何处理这些错误。
我觉得将 MVC 控制器/模型/视图状态注入回模型/服务/存储库等违反了层之间的基本分离。
归档时间: |
|
查看次数: |
1899 次 |
最近记录: |