Похоже, что циклическая ссылка была связана с тем фактом, что уровень сервиса зависел от ModelState контроллера, а контроллер - от уровня сервиса.
Мне пришлось переписать свой слой проверки, чтобы заставить это работать. Вот что я сделал.
Определите общий интерфейс валидатора, как показано ниже:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
Мы хотим иметь возможность возвращать экземпляр ValidationState, который, очевидно, определяет состояние проверки.
public class ValidationState
{
private readonly ValidationErrorCollection _errors;
public ValidationErrorCollection Errors
{
get
{
return _errors;
}
}
public bool IsValid
{
get
{
return Errors.Count == 0;
}
}
public ValidationState()
{
_errors = new ValidationErrorCollection();
}
}
Обратите внимание, что у нас есть строго типизированная коллекция ошибок, которую мы также должны определить. Коллекция будет состоять из объектов ValidationError, содержащих имя свойства проверяемой сущности и связанное с ним сообщение об ошибке. Это просто соответствует стандартному интерфейсу ModelState.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
А вот как выглядит ошибка ValidationError:
public class ValidationError
{
private string _property;
private string _message;
public string Property
{
get
{
return _property;
}
private set
{
_property = value;
}
}
public string Message
{
get
{
return _message;
}
private set
{
_message = value;
}
}
public ValidationError(string property, string message)
{
Property = property;
Message = message;
}
}
Остальное это магия StructureMap. Нам нужно создать слой службы проверки, который будет определять объекты проверки и проверять нашу сущность. Я хотел бы определить интерфейс для этого, так как я хочу, чтобы любой, кто использует службу валидации, не знал о присутствии StructureMap. Кроме того, я думаю, что разбрасывать ObjectFactory.GetInstance () где угодно, кроме логики загрузчика, плохая идея. Централизованное хранение - это хороший способ обеспечить хорошую ремонтопригодность. В любом случае, я использую шаблон декоратора здесь:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
И мы наконец-то реализовали это:
public class ValidationService : IValidationService
{
#region IValidationService Members
public IValidator<TEntity> GetValidatorFor<TEntity>(TEntity entity)
{
return ObjectFactory.GetInstance<IValidator<TEntity>>();
}
public ValidationState Validate<TEntity>(TEntity entity)
{
IValidator<TEntity> validator = GetValidatorFor(entity);
if (validator == null)
{
throw new Exception("Cannot locate validator");
}
return validator.Validate(entity);
}
#endregion
}
Я собираюсь использовать службу проверки в моем контроллере. Мы могли бы переместить его на уровень сервиса и сделать так, чтобы StructureMap использовал внедрение свойства, чтобы внедрить экземпляр ModelState контроллера в уровень сервиса, но я не хочу, чтобы уровень сервиса был связан с ModelState. Что если мы решим использовать другой метод проверки? Вот почему я бы предпочел положить его в контроллер. Вот как выглядит мой контроллер:
public class PostController : Controller
{
private IEntityService<Post> _service = null;
private IValidationService _validationService = null;
public PostController(IEntityService<Post> service, IValidationService validationService)
{
_service = service;
_validationService = validationService;
}
}
Здесь я внедряю свои сервисные уровни и экземпляры сервисов проверки с использованием StructureMap. Итак, нам нужно зарегистрировать оба в реестре StructureMap:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
Вот и все. Я не показываю, как я реализую свой PostValidator, но он просто реализует интерфейс IValidator и определяет логику валидации в методе Validate (). Все, что осталось сделать, - это вызвать экземпляр службы валидации для получения валидатора, вызвать метод validate для вашей сущности и записать любые ошибки в ModelState.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "PostId")] Post post)
{
ValidationState vst = _validationService.Validate<Post>(post);
if (!vst.IsValid)
{
foreach (ValidationError error in vst.Errors)
{
this.ModelState.AddModelError(error.Property, error.Message);
}
return View(post);
}
...
}
Надеюсь, я помог кому-то с этим:)