0 structuremap model-view-controller repository-pattern
我有一个在ado.net实体框架之上创建的存储库模式.当我试图实现StructureMap来解耦我的对象时,我一直得到StackOverflowException(无限循环?).这是模式的样子:
IEntityRepository,其中TEntity:class定义基本的CRUD成员
MyEntityRepository:IEntityRepository实现CRUD成员
IEntityService,其中TEntity:class定义返回每个成员的公共类型的CRUD成员.
MyEntityService:IEntityService使用存储库检索数据并返回一个公共类型作为结果(IList,bool等)
问题似乎与我的服务层有关.更具体地说是构造函数.
public PostService(IValidationDictionary validationDictionary)
: this(validationDictionary, new PostRepository())
{ }
public PostService(IValidationDictionary validationDictionary, IEntityRepository<Post> repository)
{
_validationDictionary = validationDictionary;
_repository = repository;
}
Run Code Online (Sandbox Code Playgroud)
从控制器,我传递一个实现IValidationDictionary的对象.我明确调用第二个构造函数来初始化存储库.
这是控制器构造器的外观(第一个创建验证对象的实例):
public PostController()
{
_service = new PostService(new ModelStateWrapper(this.ModelState));
}
public PostController(IEntityService<Post> service)
{
_service = service;
}
Run Code Online (Sandbox Code Playgroud)
如果我没有传递我的IValidationDictionary对象引用,一切都有效,在这种情况下,第一个控制器构造函数将被删除,服务对象只有一个构造函数接受存储库接口作为参数.
我感谢任何帮助:)谢谢.
小智 8
看起来循环引用与服务层依赖于Controller的ModelState和依赖于Service层的Controller这一事实有关.
我不得不重写我的验证层以使其工作.这就是我做的.
定义通用验证器接口,如下所示:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
Run Code Online (Sandbox Code Playgroud)
我们希望能够返回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();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们还有一个强类型错误集合,我们也需要定义它.该集合将包含ValidationError对象,其中包含我们正在验证的实体的属性名称以及与之关联的错误消息.这只是遵循标准的ModelState接口.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
Run Code Online (Sandbox Code Playgroud)
以下是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;
}
}
Run Code Online (Sandbox Code Playgroud)
其余的是StructureMap魔术.我们需要创建验证服务层,它将定位验证对象并验证我们的实体.我想为此定义一个接口,因为我希望任何使用验证服务的人完全不知道StructureMap的存在.此外,我认为在引导程序逻辑之外的任何地方撒上ObjectFactory.GetInstance()是一个坏主意.保持集中是确保良好可维护性的好方法.无论如何,我在这里使用装饰模式:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
Run Code Online (Sandbox Code Playgroud)
我们终于实现了它:
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
}
Run Code Online (Sandbox Code Playgroud)
我将在我的控制器中使用验证服务.我们可以将它移动到服务层并让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;
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我使用StructureMap注入我的服务层和validaton服务实例.所以,我们需要在StructureMap注册表中注册:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
Run Code Online (Sandbox Code Playgroud)
而已.我没有展示我如何实现我的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);
}
...
}
Run Code Online (Sandbox Code Playgroud)
希望我帮助这个人:)