1

MVC(3 RC2)アプリケーションで最高に達する一連のASP.NET 4プロジェクトがあります。このソリューションは、クロスカット依存性注入と検証にUnityとEntLib Validationを使用しています。両方とも、リポジトリとサービス層の実装を注入するのに最適です。entlib(またはDataAnnotations)、MVC、およびリポジトリパターンを使用して重複キー検証を実行する方法

しかし、重複したキーの検証方法を理解できません。たとえば、ユーザーが登録するときに、他のユーザーが既に使用しているユーザーIDを選択しないようにしたいとします。このタイプの検証では、検証オブジェクトにはリポジトリ参照...またはIQueryable/IEnumerable参照を取得して、すでにDBにある他の行をチェックする必要があります。

私が持っているのは、すべての適切なDataAnnotations属性とEntLib検証属性とともに、ユーザーのすべてのプロパティーセッターとゲッターを持つUserMetadataクラスです。 EF4 POCO Entity Generatorテンプレートを使用して実装されたUserEntityクラスもあります。 UserEntityはMetadataTypeAttributeを持つため、UserMetadataに依存します。同じ正確なMetadataType属性を持つUserViewModelクラスもあります。このようにして、属性を使用してエンティティとビューモデルの両方に同じ検証規則を適用できます。

リポジトリクラスへの具体的な参照はまったくありません。すべてのリポジトリはUnityを使用して注入されます。依存関係注入を取得するサービス層もあります。 MVCプロジェクトでは、サービスレイヤインプリメンテーションクラスがコントローラクラスに挿入されます(コントローラクラスにはサービスレイヤインターフェイス参照のみが含まれます)。 Unityは、リポジトリの実装をサービスレイヤクラスに挿入します(サービスクラスもインターフェイス参照のみを含みます)。

私は、メタデータクラスのDataAnnotations CustomValidationAttributeを試しました。これの問題は、検証メソッドが静的でなければならず、メソッドがリポジトリ実装を直接インスタンス化できないことです。私のリポジトリインターフェイスはIRepository <T>であり、すべてのドメインオブジェクトに対してEntityRepository <T>という1つのリポジトリ実装クラスしかありません。リポジトリを明示的にインスタンス化するには、新しいEntityRepository <UserEntity>()が必要です。これにより、循環依存グラフが生成されます:UserMetadata [依存] DuplicateUserIDValidator [依存] UserEntity [依存] UserMetadata。

カスタムの検証属性とともに、カスタムEntLibバリデーター<T>を作成しようとしました。ここで私は静的メソッドで同じ問題はありません。 UnityがEntityRepositoryをバリデータクラスに挿入する方法を見つけたら、これを動作させることができると思います。私はできません。今では、すべての検証コードが自分のメタデータクラスライブラリにあります。なぜなら、それがカスタム検証属性が行く場所なのでです。

現在のリポジトリの状態を確認する必要がある検証を実行する方法に関するアイデアはありますか?下層のクラスライブラリに依存関係を挿入するためにUnityを使用することはできますか?

+0

は、この記事を見てみましょう。 – Steven

答えて

0

さて、私は私が私の前のライブラリアーキテクチャを使用することができます、より良い解決策を見つけました。

私はUserEntityから別のライブラリにUserMetadataクラスを維持することができました。実際DuplicateUserValidator実装クラスは、私のサービス層であり、このようになります

UserMetadata DuplicateUserValidationAttribute [依存する] [に依存] IDuplicateUserValidator

public class DuplicateUserValidator : Validator<string>, IDuplicateUserValidator 
{ 
    public DuplicateUserValidator() 
     : base(null, null) 
    { 
    } 

    [Dependency] 
    public IRepository<UserEntity> UserRepository { get; set; } 

    protected override string DefaultMessageTemplate 
    { 
     get { throw new NotImplementedException(); } 
    } 

    protected override void DoValidate(string stringToValidate, object currentTarget, string key, ValidationResults validationResults) 
    { 
     if (this.UserRepository == null) 
      throw new InvalidOperationException("This should not happen"); 

     // actual code to perform validation... 
    } 
} 
ここのような依存関係グラフが見えるものです

トリックはDuplicateUserValidatorAttributeがDuplicateUserValidatorに依存しないようになっている:

public class DuplicateUserValidatorAttribute : ValidatorAttribute 
{ 
    protected override Validator DoCreateValidator(Type targetType) 
    { 
     var validator = Unity.Container.Resolve<IDuplicateUserValidator>() as Validator; 
     validator.Tag = Tag; 
     validator.MessageTemplate = GetMessageTemplate(); 
     return validator; 
    } 

} 

私がそこから行う必要があったのは、ユニティ設定へのマッピングを追加することでした。シングルトンがIDuplicateUserValidatorを解決すると、UserRepositoryプロパティの[Dependency]属性を尊重して、サービスレイヤーから実装クラスを注入します。 http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=47:

0

データベースで行います。本当にマルチユーザーに安全な唯一のソリューションです。

+0

私はDBでこれを行うことが唯一のマルチユーザセーフなオプションであるとは理解していません。私のリポジトリは、ObjectContextをEntityTransactionでラップするUnitOfWorkパターンを使用して動作します。私はDBレベルの検証を避けたいのです。なぜなら、1)この要件をテストしにくくすること、2)SQLExceptionをキャッチすること、3)データベースからヒットせずにクライアントからバリデーターを呼び出す方法がないことです。 – danludwig

+0

@olivehourでは、DBだけが独自のトランザクションの外側を見ることができます。競合するコミットされたレコードがあなたの目に見えないトランザクションにあっても、DB *のユニークな制約は期間、*失敗しません。 1)ユーザーAが競合をチェックし、見つからない、2)ユーザーBが競合をチェックしていない、3)ユーザーAが名前として「Bob」をコミットしている、4)ユーザーが競合を検出していないこと、 "B"は "Bob"を名前としてコミットします。おっとっと。 DBサーバーに組み込まれている機能をテストする必要はありません。それはあなたのコードではありません。ただし、クライアントからこれを検証することはできます。 –

+0

これで、並行性について話しています。私は、dbテーブルにタイムスタンプの列があり、アプリケーションはコミット(楽観的同時実行の実装)を行う前に、行のバージョンをチェックします。ユーザーBが「Bob」をコミットすると、タイムスタンプの値は一致せず、アプリケーションは同時性の競合があることを知ります。 DBにもIsCurrentVersion列があるので、問題の列に固有の制約を付けることはできません。古い行はすべてデータベースに保存されます。したがって、同じユーザーIDを持つDBには多数のユーザーが存在することがありますが、IsCurrentVersion == trueを持つユーザーは1人だけです。代わりにトリガが必要になります。 – danludwig

0

私は解決策を見つけましたが、それが最良のものかどうかまだ分かりません。私のような凶悪な力のように思える。

最初に私のメタデータクラスを自分のエンティティクラスと同じプロジェクト/ライブラリに結合しました。私のリポジトリインタフェースはIRepository <T>なので、リポジトリはIRepository <UserEntity>と宣言する必要がありました。これは、私が話していた循環依存を解決しました。今私はUserEntity [依存している] UserMetadata [に依存] DuplicateUserValidationAttribute [依存] DuplicateUserValidator [依存] UserEntity。これらのクラスはすべて同じライブラリにありますが、循環はまだありますが、循環アセンブリの依存関係はありません。

私がしたもう1つのことは、自分のIUnityContainerをシングルトンにラップし、それを私のクロスカッティングユーティリティライブラリ(MVC 3プロジェクトにのみ存在する前)に移動したことです。

その後、私のDuplicateUserValidatorコンストラクタで、私は次のことを行うことができます:

public class DuplicateUserValidator : Micrsoft.Practices.EnterpriseLibrary.Validation.Validator<string> 
{ 
    public IRepository<UserEntity> UserRepository { get; set; } 

    public DuplicateUserValidator(string tag) 
     : base(string.Empty, tag) 
    { 
     IUnityContainer unity = UnityContainerSingleton.Get(); 
     this.UserRepository = unity.Resolve<IRepository<UserEntity>>() as IRepository<UserEntity>; 
    } 
} 

は、今私は、カスタムentlibバリデータの実装に注入リポジトリ依存関係を持っています。私はそれを自動的にコンストラクタに挿入するための団結を得ることができれば幸いです。