看起来循环引用与服务层依赖于控制器的模型状态和控制器依赖于服务层的事实有关。
我必须重写我的验证层才能让它工作。这就是我所做的。
定义通用验证器接口,如下所示:
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);
}
...
}
希望我能帮助别人解决这个问题:)