本文介绍了我的MVC存储库模式和StructureMap存在问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在ado.net实体框架之上创建的存储库模式.当我尝试实现StructureMap来分离对象时,我不断收到StackOverflowException(无限循环?).模式如下:

I have a repository pattern i created on top of the ado.net entity framework. When i tried to implement StructureMap to decouple my objects, i kept getting StackOverflowException (infinite loop?). Here is what the pattern looks like:

IEntityRepository,其中TEntity:类定义基本的CRUD成员

IEntityRepository where TEntity : classDefines basic CRUD members

MyEntityRepository:IEntityRepository实现CRUD成员

MyEntityRepository : IEntityRepositoryImplements CRUD members

IEntityService,其中TEntity:类定义CRUD成员,为每个成员返回通用类型.

IEntityService where TEntity : classDefines CRUD members which return common types for each member.

MyEntityService:IEntityService使用存储库检索数据并返回常见类型(IList,bool等)

MyEntityService : IEntityServiceUses the repository to retrieve data and return a common type as a result (IList, bool and etc)

问题似乎出在我的服务层.更具体地讲,使用构造函数.

The problem appears to be with my Service layer. More specifically with the constructors.

    public PostService(IValidationDictionary validationDictionary)
        : this(validationDictionary, new PostRepository())
    { }

    public PostService(IValidationDictionary validationDictionary, IEntityRepository<Post> repository)
    {
        _validationDictionary = validationDictionary;
        _repository = repository;
    }

我从控制器传递了一个实现IValidationDictionary的对象.我明确地调用第二个构造函数来初始化存储库.

From the controller, i pass an object that implements IValidationDictionary. And i am explicitly calling the second constructor to initialize the repository.

这是控制器构造函数的外观(第一个创建验证对象的实例):

This is what the controller constructors look like (the first one creates an instance of the validation object):

    public PostController()
    {
        _service = new PostService(new ModelStateWrapper(this.ModelState));
    }

    public PostController(IEntityService<Post> service)
    {
        _service = service;
    }

如果我不通过我的IValidationDictionary对象引用,一切都会起作用,在这种情况下,将删除第一个控制器构造函数,并且服务对象将只有一个接受存储库接口作为参数的构造函数.

Everything works if i don't pass my IValidationDictionary object reference, in which case the first controller constructor would be removed and the service object would only have one constructor which accepts the repository interface as the parameter.

感谢您的帮助:)谢谢.

I appreciate any help with this :) Thanks.

推荐答案

循环引用似乎与服务层依赖于Controller的ModelState以及Controller依赖于Service层这一事实有关.

It looks like the circular reference had to do with the fact that the service layer was dependent on the Controller's ModelState and the Controller dependent on the Service layer.

我必须重写我的验证层才能使它起作用.这就是我所做的.

I had to rewrite my validation layer to get this to work. Here is what i did.

定义通用验证器界面,如下所示:

Define generic validator interface like below:

public interface IValidator<TEntity>
{
    ValidationState Validate(TEntity entity);
}

我们希望能够返回ValidationState的一个实例,该实例显然定义了验证的状态.

We want to be able to return an instance of ValidationState which, obviously, defines the state of validation.

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接口.

Notice that we have an strongly typed error collection which we need to define as well. The collection is going to consist of ValidationError objects containing the property name of the entity we're validating and the error message associated with it. This just follows the standard ModelState interface.

public class ValidationErrorCollection : Collection<ValidationError>
{
    public void Add(string property, string message)
    {
        Add(new ValidationError(property, message));
    }
}

这是ValidationError的样子:

And here is what the ValidationError looks like:

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的存在.此外,我认为在Bootstrapper逻辑之外的任何地方撒上ObjectFactory.GetInstance()是一个坏主意.保持集中状态是确保良好可维护性的好方法.无论如何,我在这里使用装饰器模式:

The rest of this is StructureMap magic. We need to create validation service layer which will locate validation objects and validate our entity. I'd like to define an interface for this, since i want anyone using validation service to be completely unaware of the StructureMap presence. Besides, i think sprinkling ObjectFactory.GetInstance() anywhere besides the bootstrapper logic a bad idea. Keeping it centralized is a good way to insure good maintainability. Anyway, i use the decorator pattern here:

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耦合.如果我们决定使用另一种验证技术怎么办?这就是为什么我宁愿将其放在控制器中的原因.这是我的控制器的外观:

I'm going to be using validation service in my controller. We could move it to the service layer and have StructureMap use property injection to inject an instance of controller's ModelState to the service layer, but i don't want the service layer to be coupled with ModelState. What if we decide to use another validation technique? This is why i'd rather put it in the controller. Here is what my controller looks like:

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注入我的服务层和Validaton服务实例.因此,我们需要在StructureMap注册表中都进行注册:

Here i am injecting my service layer and validaton service instances using StructureMap. So, we need to register both in StructureMap registry:

    ForRequestedType<IValidationService>()
       .TheDefaultIsConcreteType<ValidationService>();

    ForRequestedType<IValidator<Post>>()
            .TheDefaultIsConcreteType<PostValidator>();

就是这样.我没有显示如何实现我的PostValidator,但是它只是实现IValidator接口并在Validate()方法中定义验证逻辑.剩下要做的就是调用验证服务实例以检索验证器,在实体上调用validate方法,并将任何错误写入ModelState.

That's it. I don't show how i implement my PostValidator, but it's simply implementing IValidator interface and defining validation logic in the Validate() method. All that's left to do is call your validation service instance to retrieve the validator, call the validate method on your entity and write any errors to 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);
        }

        ...
    }

希望我可以帮助别人:)

Hope i helped somebody out with this :)

这篇关于我的MVC存储库模式和StructureMap存在问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-06 01:18