Похоже, что круговая ссылка связана с тем, что сервисный уровень зависел от 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;
}
}
Здесь я инъекционный мой уровень сервиса и экземпляры validaton услуг с использованием StructureMap. Поэтому нам необходимо зарегистрироваться как в реестре StructureMap:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
Всё. Я не показываю, как я реализую свой PostValidator, но он просто реализует интерфейс IValidator и определяет логику проверки в методе 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);
}
...
}
Надежда я помог кому-то с этим :)
с синтаксисом окраски немного не на StackOverflow :) TEntity это общий тип , а не объект класса. Он просто черный в Visual Studio. Я использую TEntity, чтобы просто передать тип объекта, который я хочу проверить. Я согласен с вами при проверке в контроллере. Я не мог найти лучшего способа проверить, что даст мне большую гибкость. Выполняя это таким образом, я могу использовать любую фреймворк проверки, который я хочу, в моем валидаторе объектов. – 2009-07-31 18:11:37
Теперь я не думал об этом, но что может работать с использованием шаблона адаптера для написания адаптера для ModelState. Таким образом, вы бы подтвердили свою сущность, получили объект ValidationState и использовали адаптер для преобразования его в ModelState. Если вы достаточно абстрактны, вы можете использовать StructureMap для инъекции адаптера по вашему выбору. Я не уверен, насколько хорошо он будет связан с вашим контроллером, но я уверен, что вы могли бы разделить его довольно красиво. Таким образом, ваша служба проверки может, вероятно, использоваться в ваших бизнес-моделях/сущностях или уровне обслуживания. Я посмотрю, смогу ли я с этим что-то сделать. – 2009-07-31 18:12:18
Я смотрел после этого, http://www.asp.net/Learn/mvc/tutorial-38-cs.aspx. Первоначально я отклонил этот вариант, так как считал, что он будет слишком сильно связывать мой сервисный уровень с MVC, но в тот момент, ради того, чтобы мой проект продвигался, он выглядит как лучший из плохой группы. Однако я очень хочу найти более свободную альтернативу. Если вы придумаете что-нибудь, напишите мне письмо на lloyd phillips на одно слово в xtra dot co dot nz. С уважением Lloyd – 2009-08-02 22:32:22