简体   繁体   English

将ASP.NET MVC Controller属性注入服务层依赖项?

[英]Inject ASP.NET MVC Controller property into service layer dependency?

I am using an approach similar to the one in this ASP.NET MVC tutorial where you pass a wrapper around a controller's ModelState collection into a validation class so that the controller can access error information. 我使用的方法类似于ASP.NET MVC教程中的方法 ,您将控制器的ModelState集合中的包装器传递给验证类,以便控制器可以访问错误信息。

Here is a cooked up example: 这是一个熟练的例子:

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) { }
}

Using the Castle Windsor container for IoC/DI, how do I create the IProductService ? 使用Castle Windsor容器进行IoC / DI,如何创建IProductService Typically, I would have: 通常,我会:

MvcApplication.IocContainer.Resolve<IProductService>()

but this isn't able to inject the value of the Controller's ModelState property into the constructor for ProductValidator. 但是这不能将Controller的ModelState属性的值注入到ProductValidator的构造函数中。 I could possibly wire this up using constructor parameters, but that seems really ugly. 我可以使用构造函数参数来连接它,但这看起来真的很难看。

I'm assuming you want the modelstate passed in to auto inject any errors into your model? 我假设您希望传入的模型状态自动将任何错误注入模型中? IMHO, ModelState should stay where it is, and you bring the validation errors to it. 恕我直言,ModelState应该保持原样,并将验证错误带到它。 Here's how I handle errors as an example. 以下是我如何处理错误的例子。 I'm not saying this is the best way or the only way, but it is one way where your validation layer doesn't have to have knowledge of who or what consumes validation errors. 我不是说这是最好的方式或唯一的方法,但它是一种方法,您的验证层不必知道谁或什么消耗验证错误。

First, in my poco, I use System.ComponentModel.DataAnnotations for validation rules. 首先,在我的poco中,我使用System.ComponentModel.DataAnnotations作为验证规则。 Here is my account class, for example. 例如,这是我的帐户类。

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; }
}

Because I want to be able to initiate validation myself (outside of MVC doing it on it's own), I had to implement my own validator. 因为我希望能够自己启动验证(在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());
    }
}

Here is the ValidationResult I pass back 这是我传回的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();
    }

}

ErrorInfo is a very basic class with information about my error 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;
    } 
}

in order to wrap this validation up all nice and neat with my poco classes, I inherit from a base class. 为了将这个验证包装好我的poco类,我继承了一个基类。 For validation to make it generic where the inherited child has to tell the base class it's type. 对于验证,使其成为通用的继承子必须告诉基类的类型。 It feels circular, but it works. 它感觉循环,但它的工作原理。

[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
}

And finally, as you can see, my validation throws a RulesException. 最后,正如您所看到的,我的验证会抛出一个RulesException。 This class is a wrapper for all the errors. 此类是所有错误的包装器。

[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) } )
    {

    }
}

So, with that said, my validation in my controller looks more like this 所以,说到这一点,我在控制器中的验证看起来更像是这样

public ActionResult MyAction()
{
   try
   {
      //call validation here
   }
   catch (RulesException ex)
   {
      ModelState.AddModelStateErrors(ex);
   }

   return View();
}

ModelState.AddModelStateErrors(ex); ModelState.AddModelStateErrors(前); is an extension method that I wrote. 是我写的扩展方法。 it is very simple. 这很简单。

    public static void AddModelStateErrors(this System.Web.Mvc.ModelStateDictionary modelState, RulesException exception)
    {
        foreach (ErrorInfo info in exception.Errors)
        {
            modelState.AddModelError(info.PropertyName, info.ErrorMessage);
        }
    }

This way, I can still use DI for my services/repositories, and let them throw up an error when my model is invalid. 这样,我仍然可以将DI用于我的服务/存储库,并在模型无效时让它们抛出错误。 Then I let the front end - whether that is an MVC app, web service, or windows app - decide what to do with those errors. 然后我让前端 - 无论是MVC应用程序,Web服务还是Windows应用程序 - 决定如何处理这些错误。

I feel that injecting MVC controller/model/view state back into the model/services/repositories/etc is a violation of the basic separation between the layers. 我觉得将MVC控制器/模型/视图状态注入模型/ services / repositories / etc是违反了层之间的基本分离。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM