0

ado.net エンティティ フレームワークの上に作成したリポジトリ パターンがあります。オブジェクトを切り離すために StructureMap を実装しようとすると、StackOverflowException (無限ループ?) が発生し続けました。パターンは次のようになります。

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

コントローラーから、IValidationDictionary を実装するオブジェクトを渡します。そして、リポジトリを初期化するために 2 番目のコンストラクターを明示的に呼び出しています。

コントローラーのコンストラクターは次のようになります (最初のコンストラクターは、検証オブジェクトのインスタンスを作成します)。

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

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

IValidationDictionary オブジェクト参照を渡さない場合、すべてが機能します。この場合、最初のコントローラー コンストラクターが削除され、サービス オブジェクトには、パラメーターとしてリポジトリ インターフェイスを受け入れるコンストラクターが 1 つだけ含まれます。

これについての助けに感謝します:)ありがとう。

4

3 に答える 3

8

循環参照は、サービスレイヤーがコントローラーの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;
    }
}

ここでは、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);
        }

        ...
    }

私がこれで誰かを助けたことを願っています:)

于 2009-05-29T01:38:38.777 に答える
1

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
}

エラーをディクショナリからモデルの状態にコピーするためのコントローラーのコード。Controllerの拡張機能としてはこれが一番いいかもしれません。

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);
    }
}
于 2010-12-19T19:52:46.610 に答える