0

私は EF5 を使用しており、POCO クラスにエンティティがあります。最初の質問は、ビジネス ルールと検証を実装するのに最適な場所はどこですか?

私の最初の推測は、SaveChanges() がトリガーされたときに DBContext から呼び出される Validate() 関数の POCO クラスに直接配置することです。これはうまく機能しますが、一部のルールでは、クラス Invoice の次の例のように、複数のエンティティにわたる検証が必要です。

if(this.Items.Where(i=>i.Price > 100).Count() > 0)
{
    //mark invoice for review
    this.IsForReview = true;
}

次に、単体テストで Validation 関数を (各ビジネス ルールに対して) テストしますが、請求書クラスにアイテムを入力する必要もあります (そうしないと、常に空になります)。

もう 1 つのアイデアは、個別の検証関数 (または各ルールごとのクラス) を使用して InvoiceValidation クラスを作成することです。これは単体テストが容易ですが、維持するファイル/クラスの数が増えます。

既存のソリューションへの提案やリンクをいただければ幸いです

4

3 に答える 3

1

最善の方法は、依存関係によって異なります。POCO /コアアセンブリはEFに依存していますか?DBへのアクセスをコアライブラリアセンブリに注入しますか?等

私は個人的にrepository/luwパターンを使用しています。このパターンでは、さまざまなリポジトリオブジェクトが汎用のベースリポジトリオブジェクトから継承されます。DALはEFに依存しますが、コアのPOCOクラスは依存しません。

リポジトリサブクラスには特定のタイプがあり、他のOBEJCTビジネスチェックを実行します。他のエンティティをチェックする必要があるIEビジネスルール、私はDALに実装します。

リポジトリクラスはデータアクセス層プロジェクトに属し、DOはEFに依存しており、コンテキストが挿入されています。以下の例。

POCOで実行するインスタンスに固有のチェック。DBアクセスを必要とするチェックは、必要に応じてオーバーライドされる基本クラスリポジトリクラスに実装されたインターフェイスを介して実行します。そのため、オブジェクトを追加または変更すると、 CheckEntityの呼び出しがトリガーされるようになりました。

例...例を適切に保つために一部のコードが削除されていることに注意してください...

 public class RepositoryEntityBase<T> : IRepositoryEntityBase<T>, IRepositoryEF<T> where T : BaseObject
public virtual OperationStatus Add(T entity)
    {
        var opStatus = new OperationStatus(status: true, operation: OperationType.Add);
        try
        {
            if (OnBeforeAdd != null) // registered listeners of added event?
            {
                var evtArg = PrepareEventArgs(entity, MasterEventType.Create);
                OnBeforeAdd(this, evtArg);
            }
            opStatus = CheckBeforePersist(entity);
            if (opStatus.Status)
            {
                Initialize(entity);
                EntityDbSet.Add(entity);

                if (OnAfterAdd != null) // registered listeners of added event?
                {
                    var evtArg = PrepareEventArgs(entity, MasterEventType.Create);
                    OnAfterAdd(this, evtArg);
                }
            }
        }
        catch (Exception ex)
        {
            opStatus.SetFromException("Error Adding " + typeof(T), ex);
        }
        return opStatus;
    }

//... then in a specific repository class



//... irepositorybase expects Check before persist.

  public override OperationStatus CheckBeforePersist(MasterUser entity)
    {

        // base entity rule check first
        var opStatus = new OperationStatus(true, OperationType.Check);          
        opStatus.ValidationResults  = base.CheckEntity(entity);      
        if (opStatus.ValidationResults.Count > 0)
        {
            opStatus.Status = false;
            opStatus.Message = "Validation Errors";
            return opStatus;
        }


        //now check the local memory
        var masterUser = Context.Set<MasterUser>().Local   //in context
                                              .Where(mu => mu.Id != entity.Id // not this record
                                                    &&     mu.UserName == entity.UserName ) // same name
                                              .FirstOrDefault();
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate UserName :" + masterUser.UserName + " UserId:"+ masterUser.Id.ToString();
            return opStatus;
        }
        masterUser = Context.Set<MasterUser>().Local   //in context
                                             .Where(mu => mu.Id != entity.Id // not this record
                                                   && mu.Email == entity.Email) // same email
                                             .FirstOrDefault();
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate Email :" + masterUser.Email + " Username:" + masterUser.UserName;
            return opStatus;
        }                                               

        // now check DB
        masterUser = Get(mu => mu.Id != entity.Id             //not this record being checked
                          && mu.UserName == entity.UserName);     // has same username
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate UserName :" + masterUser.UserName + " UserId:"+ masterUser.Id.ToString();
            return opStatus;
        }
        masterUser = Get(mu => mu.Id != entity.Id    // not this record
                      && mu.Email == entity.Email);  // but same email 
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate Email:" + masterUser.Email + " UserName:"+ masterUser.UserName;
            return opStatus;
        }
        return opStatus;   
    }

}
于 2013-01-14T21:52:23.417 に答える
0

誰かが興味を持っているなら、これは私がこれまでに見つけた最良の例です:

http://codeinsanity.com/archive/2008/12/02/a-framework-for-validation-and-business-rules.aspx

于 2013-03-15T19:08:33.787 に答える
0

Fluent Validation (http://fluentvalidation.codeplex.com/) のようなものをお勧めします。これにより、ルールのコレクションを取得して、検証対象の POCO クラスとは別の単一のコンテキストにまとめることができます。

于 2013-01-14T21:35:15.593 に答える