
この記事は
前半の続きです。 投稿では、式のバイナリツリーの形式の型のオブジェクトモデルと、SQLクエリのパラメーター化とリフレクション最適化の関連トピックを使用して、SQLクエリを構築することを検討します。 この記事のトピックはそれ自体非常に分離されているため、特に最初の部分に注意を払うことなく読むことができます。 繰り返しになりますが、この決定は「夕方に行われました-何もすることはありませんでした」ことであり、工業製品の栄冠を主張していません。
いくつかの歌詞または動的SQLパラメーター化について
一般に、動的SQLは、ストアドプロシージャとして実装されていないDBMSを実行するためにクライアントから送信されるスクリプトです。 DBMSは、EXEC()およびsp_executesql命令を使用してこのようなスクリプトを実行します。
一部では予期しないように見えるかもしれませんが、6番目のバージョン以降、SQL Serverは動的クエリをキャッシュできます。 ただし、すべてがそれほど単純ではありません。 ストアドプロシージャのキャッシュでの検索中、SQL Serverはその名前をキーとして使用します。動的SQLの場合、名前は使用できないため、SQLは検索キーの形式でパラメーターを含むすべてのクエリテキストを使用します。 はい、絶対にリクエストの全文、カール! スペースを使用し、大文字と小文字を区別せず、コメント付き。
さらに、サーバーは、その設計に従ってキャッシュ内の要求を検索します。 したがって、スキームに従ってテーブルの完全な名前を指定することが非常に重要です。
パラメータ化されたクエリ cmd.CommandText = "SELECT mycol FROM dbo.product WHERE col = @value"; cmd.Parameters.Add("@value", SqlDbType.Int); cmd.Parameters["@value"].Value = value;
プロジェクトに多数のパラメータ化されていないクエリがプロジェクト全体に散在している場合、1つの関数へのカプセル化を使用したリファクタリングを検討する必要があります。 ORMを使用すると、このような問題を解決できます(手動SQLを使用するORMの場合、カプセル化を自分で行う必要があります)。
パラメータ化されていないクエリには、SQLインジェクションなどの多くの副作用が伴うことを忘れないでください。 動的SQLキャッシングの問題については、
こちらをご覧ください 。
何が欲しい?
式ツリーに基づいてデータを取得するためのリポジトリメソッドを定義します。 クライアントは次のようになります:
var repo = new ProfileRepository(); var profiles = repo.Get(x => x.Id == id && x.Rating > rate)
式ツリーベースのSQL生成には、次の利点があります。
- ユーザーがデータベース内の列の文字列名を思い出す必要がなくなります
- ユーザーが間違った列名を指定すると、別のレジスタで例外が発生したり、スペースを含む列名が指定されたりするため、SQL Server側でキャッシュの問題が発生する
- フィルタリング条件はコンポジットにすることができます。これにより、.NET Expressionクラスを実装できます
欠点は、バイナリツリーを再帰的に走査するSQL生成の複雑さとパフォーマンスです。
少し反映する
オブジェクトのマッピング中に、可能であればスローリフレクションメカニズムを避けながら、プロパティと属性を動的に取得する必要があります。 Richterは値の取得とデリゲートに基づいたプロパティの値の設定のパフォーマンスを最適化する方法について詳しく説明しています。ここでは詳しく説明しませんが、すぐにPropertyInfoクラスのラッパーを実装します。
型プロパティを使用した動的な作業には、次のものが必要です。
- プロパティメソッド
- プロパティ設定方法
- 物件名
- 物件タイプ
- ビジネスオブジェクトのプロパティをテーブルのフィールドにバインドするための属性
クラスPropWrapper public class PropWrapper { private readonly PropertyInfo _property; public Type Type { get { return _property.PropertyType; } } public string Name { get { return _property.Name; } }
次に、カプセル化クラス型全体を実装します。 属性、プロパティ名、プロパティタイプなどは、クラスの特定のインスタンスではなく、タイプのみに依存することに注意してください。 したがって、型構造をすぐにキャッシュすると便利です。
CacheTypeReflectionWrapperクラスの実装 internal static class CacheTypeReflectionWrapper { private static readonly Dictionary<Type, ICollection<PropWrapper>> TypesByProp = new Dictionary<Type, ICollection<PropWrapper>>(); public static ICollection<PropWrapper> GetProps(Type type) {
メインコースの調理
最後に、オブジェクトモデルからSQLマッパーの準備を開始できます。 LINQ.NETプロバイダーを使用してソリューションを実装できることにすぐに気付きましたが、まだ実装していません。
オブジェクトのフィールドにより、選択リクエストの本文を計算します。 データベーススキームに従って、フルネームを取得するためにビジネスオブジェクトのタイプとデータベースの名前が必要なのはなぜですか。
CreateBodyメソッド private static string CreateBody(string dbName, Type type) {
これで楽しい部分が始まります。-単語WHEREの後のSQL条件の生成。 .NETで式ツリーを簡単にたどるために、 ExpressionVisitorクラスがあります。 しかし、自転車を作るなら、最大限に! そのため、箱から出してすぐに資金なしで行うことができます。
式の分析は、 バイナリ式ツリーに基づいて実行されます 。
バイナリ式ツリーは、式を表すために使用される特定のタイプのバイナリツリーです。 式の二分木は、代数的および論理的な値(単項演算子および二項演算子)にすることができます。 バイナリツリーの各ノード、したがって表現のバイナリツリーには、0、1、または2つの子があります。
式ツリーには、さまざまなタイプの頂点を含めることができます。直接BinaryExpression、
MemberExpression、ConstantExpression、UnaryExpressionなど。
リーフに到達することが重要です。リーフの場合、タイプはMemberExpression、ConstantExpressionです。 MemberExpressionタイプの頂点にはフィールドが含まれており、条件オペランドは頂点のタイプによって取得できます。 ConstantExpression型の頂点には、オペランドの値が直接含まれます。
式の形式を次のようにします。
repo.Get(x => x.RoleId == 2 && x.UserInfoId > 4 && x.Id < 6)
わかりやすくするために、この場合のバイナリ式ツリーの図を示します。 頂点の値は、トラバーサルアルゴリズム中にデバッガから取得されます。

図では、フィールド{x.UserInfoId}はNULL入力可能なタイプであるため、このような頂点はUnaryExpressionです。 UnaryExpressionなどの頂点には、左と右の2つの子が含まれていません。 この場合、ConstantExpression型への変換により、オペランドの値を取得できます。
式ツリーにSQL条件の構築を実装するための機能コード、詳細なコメント付き:
その結果、身体と魂をクエリ条件と組み合わせて、次の関数を取得します。
public static string Create<T>(IDbCommand command, BinaryExpression exp) where T : class, IEntity, new() { var type = typeof(T); var selectBody = CreateBody(command.Connection.Database, type); return string.Format("{0} WHERE {1}", selectBody, CommonCommandBuilder.BuildClauseByExpression(command, type, exp)); }
実装の詳細はすべてgithubで確認できます。