代码之家  ›  专栏  ›  技术社区  ›  Serge

我的MVC存储库模式和结构映射存在问题

  •  0
  • Serge  · 技术社区  · 15 年前

    我在ADO.NET实体框架之上创建了一个存储库模式。当我试图实现structuremap以分离对象时,我总是得到stackOverflowException(无限循环?)。模式如下:

    Entity:Class所在的IEntityRepository 定义基本CRUD成员

    MyEntityRepository:IEntityRepository 实现CRUD成员

    IEntityService,其中Tentity:类 定义为每个成员返回公共类型的CRUD成员。

    MyEntityService:客户服务 使用存储库检索数据并返回一个公共类型(ilist、bool等)

    问题似乎出在我的服务层上。更具体地说,是与构造函数有关。

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

    从控制器中,我传递一个实现IValidationDictionary的对象。我显式地调用第二个构造函数来初始化存储库。

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

        public PostController()
        {
            _service = new PostService(new ModelStateWrapper(this.ModelState));
        }
    
        public PostController(IEntityService<Post> service)
        {
            _service = service;
        }
    

    如果我不传递IValidationDictionary对象引用,一切都会正常工作,在这种情况下,第一个控制器构造函数将被删除,而服务对象将只有一个接受存储库接口作为参数的构造函数。

    我非常感谢您的帮助:)谢谢。

    3 回复  |  直到 15 年前
        1
  •  8
  •   Serge    15 年前

    看起来循环引用与服务层依赖于控制器的模型状态和控制器依赖于服务层这一事实有关。

    我必须重写我的验证层才能使它工作。这是我做的。

    如下定义通用验证器接口:

    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;
        }
    }
    

    剩下的就是结构图魔法。我们需要创建验证服务层,它将定位验证对象并验证我们的实体。我想为此定义一个接口,因为我希望使用验证服务的任何人都完全不知道结构映射的存在。此外,除了引导程序逻辑之外,我认为在任何地方喷洒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
    }
    

    我将在我的控制器中使用验证服务。我们可以将它移动到服务层,并让结构映射使用属性注入将控制器的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);
            }
    
            ...
        }
    

    希望我能帮上忙:)

        2
  •  1
  •   cedd    14 年前

    我使用了一个类似的解决方案,它涉及到ivalidationDictionary的通用实现者,它使用一个StringDictionary,然后将错误从这里复制回控制器中的模型状态。

    validationDictionary接口

       public interface IValidationDictionary
        {
            bool IsValid{get;}
            void AddError(string Key, string errorMessage);
            StringDictionary errors { get; }
        }
    

    实现不引用模型状态或任何其他内容的验证字典,以便结构映射可以轻松创建它

    public class ValidationDictionary : IValidationDictionary
    {
    
        private StringDictionary _errors = new StringDictionary();
    
        #region IValidationDictionary Members
    
        public void AddError(string key, string errorMessage)
        {
            _errors.Add(key, errorMessage);
        }
    
        public bool IsValid
        {
            get { return (_errors.Count == 0); }
        }
    
        public StringDictionary errors
        {
            get { return _errors; }
        }
    
        #endregion
    }
    

    在控制器中编写代码,将错误从字典复制到模型状态。作为控制器的扩展函数,这可能是最好的。

    protected void copyValidationDictionaryToModelState()
    {
        // this copies the errors into viewstate
        foreach (DictionaryEntry error in _service.validationdictionary.errors)
        {
            ModelState.AddModelError((string)error.Key, (string)error.Value);
        }
    }
    

    因此,引导代码是这样的

    public static void BootstrapStructureMap()
    {
        // Initialize the static ObjectFactory container
        ObjectFactory.Initialize(x =>
        {
            x.For<IContactRepository>().Use<EntityContactManagerRepository>();
            x.For<IValidationDictionary>().Use<ValidationDictionary>();
            x.For<IContactManagerService>().Use<ContactManagerService>(); 
        });
    }
    

    创建控制器的代码如下

    public class IocControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return (Controller)ObjectFactory.GetInstance(controllerType);
        }
    }
    
        3
  •  0
  •   Lloyd Phillips    15 年前

    只需快速查询一下。这帮了我很大的忙,所以感谢你提出了答案,但我想知道在哪个名称空间存在帐篷?我看到Collection(Tentity)需要System.Collections.ObjectModel。我的文件编译时没有任何进一步的内容,但我看到您的Tentity引用以蓝色突出显示,这表明它具有类类型,我的在Visual Studio中是黑色的。希望你能帮忙。我很想把这个工作做好。

    您是否找到了将验证分离到服务层的方法?我的直觉告诉我,在控制器中验证有点难闻,但是我一直在寻找一种方法来将验证错误消息传递回控制器,而不将服务层与控制器紧密耦合,因此找不到任何东西。:(

    再次感谢你的出色表现!

    劳埃德