Ввести свойство ASP.NET MVC Controller в зависимость уровня обслуживания? - PullRequest
9 голосов
/ 06 октября 2010

Я использую подход, аналогичный описанному в этом 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) { }
}

Как использовать контейнер Castle Windsor для IoC / DI, как мне создать IProductService? Как правило, я бы имел:

MvcApplication.IocContainer.Resolve<IProductService>()

, но это не может вставить значение свойства ModelState контроллера в конструктор для ProductValidator. Я мог бы подключить это, используя параметры конструктора, но это выглядит ужасно.

1 Ответ

2 голосов
/ 28 октября 2010

Я предполагаю, что вы хотите, чтобы передаваемое состояние модели автоматически добавляло какие-либо ошибки в вашу модель?ИМХО, 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; }
}

Поскольку я хочу иметь возможность инициировать валидацию самостоятельно (за исключением того, что 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());
    }
}

Вот 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 - очень простой класс с информацией о моей ошибке

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

, чтобы завершить эту проверку всехс моими классами поко, я унаследован от базового класса.Для валидации, чтобы сделать ее родовой, где унаследованный потомок должен сообщить базовому классу, что это за тип.Это кажется круглым, но это работает.

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

И, наконец, как вы можете видеть, моя проверка выдает исключение 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) } )
    {

    }
}

Итак, с учетом сказанного моя проверка в моем контроллере выглядит примерно так:

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

   return View();
}

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

Таким образом, я все еще могу использовать DI для своих сервисов / репозиториев и позволить им выдавать ошибку, когда моя модель недействительна.Затем я позволил внешнему интерфейсу - будь то приложение MVC, веб-служба или приложение Windows - решить, что делать с этими ошибками.

Я чувствую, что введение контроллера / модели / представления MVC обратно в модель/ services / repositories / etc - это нарушение основного разделения между слоями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...