質問

私は、歯科医が特定の臨床活動に関する情報を取得できるようにするアプリケーションに取り組んでいます。アプリケーションは高度にカスタマイズ可能ではありませんが(カスタムワークフローやフォームはありません)、基本的なカスタマイズ機能を提供します。クライアントは、定義済みのフォームフィールドを独自のカスタムフィールドで拡張することを選択できます。管理者が作成できるフィールドタイプは約半ダースです(テキスト、日付、数値、ドロップダウンなど)。この機能をモデル化するために、永続化側でEntity-Attribute-Value(EAV)を使用しています。

アプリケーションの他の重要な機能の1つは、これらのカスタムフィールドに対してカスタムクエリを作成する機能です。これは、任意の数のルール(Date <!> lt; =(Now-5 Days)、Text Like '444'、DropDown == 'ICU')を作成できるUIを介して実現されます。すべてのルールは、クエリを作成するためにANDで結合されます。

現在の実装(I <!> quot; inherited <!> quot;)は、オブジェクト指向でも単体テストでもありません。基本的に、単一の<!> quot; God <!> quot;があります。すべての無数のルールタイプを直接複雑な動的SQLステートメント(つまり、内部結合、外部結合、および副選択)にコンパイルするクラス。このアプローチはいくつかの理由で面倒です:

  • 個別のルールを単体でテストするユニット ほぼ不可能
  • 最後のポイントは、 将来は間違いなく違反します オープンクローズド原則。
  • ビジネスロジックと永続性の懸念が混在しています。
  • 実際のデータベースが必要なため、ユニットテストの実行が遅くなります(SQLLiteはT-SQLを解析できず、パーサーをモックアウトするのは非常に困難です)

クエリのパフォーマンスをかなり維持しながら、柔軟性があり、保守可能でテスト可能な置換デザインを考えています。 OOADベースの実装では、データフィルタリングロジックの少なくとも一部をデータベースサーバーから(.NET)アプリケーションサーバーに移動すると想像するため、この最後のポイントが重要です。

私は、コマンドと責任の連鎖パターンの組み合わせを検討しています:

Queryクラスには、抽象ルールクラス(DateRule、TextRuleなど)のコレクションが含まれます。フィルタリングされていないデータのセットを含むDataSetクラスへの参照を保持します。 DataSetは永続性にとらわれない方法でモデル化されます(つまり、データベースタイプへの参照やフックはありません)

Ruleには、DataSetを受け取り、適切にフィルタリングして呼び出し元に返す単一のFilter()メソッドがあります。 Queryクラスは単純に各ルールを反復処理するため、各ルールが適切と思われるようにDataSetをフィルタリングできます。すべてのルールが実行されるか、DataSetが何もフィルターされなくなると、実行は停止します。

このアプローチについて心配していることの1つは、.NETでフィルタリングされていない可能性のある大きなデータセットを解析することのパフォーマンスへの影響です。確かに、この種の問題を解決するための、保守性とパフォーマンスのバランスがとれた、試行された真のアプローチがいくつかありますか?

最後に、管理者はNHibernateの使用を許可しません。 Linq to SQLは可能かもしれませんが、そのテクノロジーが目の前のタスクにどのように適用できるかわかりません。

多くの感謝と私は皆のフィードバックを楽しみにしています!

更新:まだこれに関する解決策を探しています。

役に立ちましたか?

解決

LINQ to SQLは、おそらくVS2008サンプルのDynamic LINQと組み合わせた理想的なソリューションになると思います。 LINQを使用して、特にIEnumerable / IQueryableの拡張メソッドを使用すると、取得する入力に応じて標準およびカスタムロジックを使用してクエリを構築できます。私はこのテクニックを多用して、MVCアクションの多くにフィルターを実装して、大きな効果を上げています。式ツリーを実際に構築し、それを使用してクエリを具体化する必要がある時点でSQLを生成するため、ほとんどの重い作業は依然としてSQLサーバーによって行われるため、シナリオに最適だと思います。 LINQが最適でないクエリを生成することが判明した場合、最適化されたクエリを利用する方法として、常にLINQデータコンテキストに追加されたテーブル値関数またはストアドプロシージャを使用できます。

更新 PredicateBuilder を使用することもできます。簡単に言えばC#3.0から。

例:タイトルに一連の検索用語の1つが含まれ、出版社がO'Reillyであるすべての書籍を検索します。

 var predicate = PredicateBuilder.True<Book>();
 predicate = predicate.And( b => b.Publisher == "O'Reilly" );
 var titlePredicate = PredicateBuilder.False<Book>();
 foreach (var term in searchTerms)
 {
     titlePredicate = titlePredicate.Or( b => b.Title.Contains( term ) );
 }
 predicate = predicate.And( titlePredicate );

 var books = dc.Book.Where( predicate );

他のヒント

私が見た方法は、ユーザーがクエリを構築する各条件をモデル化するオブジェクトを作成し、それらを使用してオブジェクトのツリーを構築することです。

オブジェクトのツリーから、クエリを満たすSQLステートメントを再帰的に構築できるはずです。

必要な基本的なオブジェクトは、ANDおよびORオブジェクト、およびEQUALS、LESSTHANなどのモデル比較用のオブジェクトです。より簡単に。

簡単な例:

public interface IQueryItem
{
    public String GenerateSQL();
}


public class AndQueryItem : IQueryItem
{
    private IQueryItem _FirstItem;
    private IQueryItem _SecondItem;

    // Properties and the like

    public String GenerateSQL()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(_FirstItem.GenerateSQL());
        builder.Append(" AND ");
        builder.Append(_SecondItem.GenerateSQL());

        return builder.ToString();
    }
}

この方法で実装すると、ルールを簡単にユニットテストできます。

マイナス面では、この解決策はまだ多くの仕事をするためにデータベースを残します。それはあなたが本当にやりたくないように聞こえます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top