2009-05-25 12 views
0

私はado.netエンティティフレームワークの上に作成されたリポジトリパターンを持っています。オブジェクトをデカップリングするためにStructureMapを実装しようとすると、StackOverflowException(無限ループ?)が発生し続けました。クラス 基本的なCRUD部材MVCリポジトリパターンとStructureMapの問題

MyEntityRepositoryを定義します:クラス 一般的なタイプを返すCRUDメンバーを定義します:IEntityRepository は、CRUD用部材TEntityが

IEntityServiceを実装TEntityが

IEntityRepository:ここではパターンは次のようになります各メンバーについて

MyEntityService:IEntityService は

問題は私のサービス層であるように思われる(IListを、ブールおよびなど)のデータを取得し、その結果として、共通の型を返すようにリポジトリを使用します。より具体的には、コンストラクタを使用します。

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

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

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

これは、コントローラのコンストラクタは、(最初​​のものは検証オブジェクトのインスタンスを作成します)どのように見えるかです:最初、その場合には、私は、私のIValidationDictionaryオブジェクト参照を渡さない場合

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

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

すべてが動作しますコントローラ・コンストラクタは削除され、サービス・オブジェクトはリポジトリ・インタフェースをパラメータとして受け入れるコンストラクタを1つだけ持つことになります。

このお手伝いを感謝します:)ありがとう。

答えて

8

循環参照は、サービスレイヤがコントローラのModelStateとServiceレイヤに依存するコントローラに依存しているという事実と関係があります。

有効にするには、検証レイヤーを書き直す必要がありました。ここに私がしたことがあります。

以下のような一般的なバリインタフェース定義します。私たちは、明らかに、検証の状態を定義し、ValidationStateのインスタンスを返すことができるようにしたい

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

を。

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 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魔法である:ここ

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

とValidationErrorをどのように見えるかです。検証オブジェクトを探し、エンティティを検証する検証サービスレイヤーを作成する必要があります。バリデーションサービスを使用している誰かがStructureMapの存在を完全に意識しないようにしたいので、私はこのためのインターフェースを定義したいと思います。また、私はObjectFactory.GetInstance()をブートストラップロジック以外の場所に振りかけるのは悪い考えだと思います。それを集中管理することは、メンテナンス性を保証する良い方法です。とにかく、私はここでDecoratorパターンを使用します。

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

     ... 
    } 

希望私はこれでこの:)

0

だけで簡単にクエリを使用して誰かを助けました。答えが出てくれてありがとうございましたが、TEntityがどのネームスペースに存在するのか疑問に思っていましたか? Colletion(TEntity)にはSystem.Collections.ObjectModelが必要です。私のファイルは何も追加せずにコンパイルしますが、私はあなたのTEntity参照が青で強調表示されていることを示しています。これはクラスタイプを示しています。あなたが手伝ってくれることを願います。私はこの仕事をするのにはかなり熱心です。

サービス層への検証を別に行う方法はありますか。コントローラでの検証はやや臭いですが、コントローラにサービスレイヤーを強く結合せずに、何も見つからないと、検証エラーメッセージをコントローラに戻す方法が見つかったと思います。 :(

は再び、偉大なポストに感謝!

ロイド

+0

のようなものであることは一般的なタイプですクラスオブジェクトではありません。 Visual Studioでは単純に黒です。私はTEntityを使用して、単に検証したいオブジェクトの型を渡します。 私はコントローラの検証についてあなたに同意します。それを検証するためのより良い方法が見つからず、柔軟性が増しました。このようにして、私は自分のエンティティバリデーター内で望むバリデーションフレームワークを使うことができます。 –

+0

今、私はそれほど心配していませんが、アダプタパターンを使用してModelState用のアダプタを作成することができます。したがって、本質的にあなたのエンティティを検証し、ValidationStateオブジェクトを取得し、アダプタを使用してModelStateに変換します。それを十分に抽象化したら、StructureMapを使用して選択したアダプタを注入することができます。あなたのコントローラーでどれくらいうまくつながっているかは分かりませんが、かなりうまくデカップルできると確信しています。このようにして、あなたの検証サービスは、あなたのビジネスモデル/エンティティまたはサービス層内でおそらく使用される可能性があります。私はこれで何かできるかどうかを見ていきます。 –

+0

私はこれに続いて、http://www.asp.net/Learn/mvc/tutorial-38-cs.aspxを見ていました。もともと、私はサービスレイヤーをMVCにあまりにも強く結びつけると考えていたので、このオプションを割り引いたのですが、分かりましたが、私のプロジェクトを進めるために、悪い束のように見えます。私は非常にゆるい代替案を見つけたいと思っています。あなたが何かを思い付くなら、私はロイドフィリップスで電子メールをドロップしてください。xtra dot co dot nz。 よろしくお願いいたします。 ロイド –

1

私はIValidationDictionaryの一般的な実装を含む同様のソリューションは、StringDictionaryを使用して使用し、その後にモデル状態にこのバックからエラーをコピー検証dictionarの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>(); 
    }); 
} 

とコードのようなものです構文の色付けが少しオフにStackOverflow :) TEntityにあるこの

public class IocControllerFactory : DefaultControllerFactory 
{ 
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
    { 
     return (Controller)ObjectFactory.GetInstance(controllerType); 
    } 
} 
関連する問題