Entity Frameworkは、データベースを使用したシステムの開発を大幅に促進します。 このフレームワークの長所と短所については議論しませんが(もちろんその多くはありますが)、このようなシステムを開発する際に解決しなければならなかった実際的な問題の1つを検討します。
かなり多数のレコードを持つ
SQLiteデータベースがあり、このデータベースが
System.Data.SQLiteおよび
Entity Framework 6.0を介して
.NETアプリケーションで使用されているとし
ます 。 そして顧客は来て、標準の正規表現を使用して検索できるように、データベース内のレコードを検索するための新しい関数が必要だと言います。
この記事では、
Linq-リクエストで指定された正規表現の処理がサーバー側で行われることを実現した方法を説明します。これにより、処理が高速化され、すべてのデータの事前ダウンロードによるクライアントアプリケーションメモリの無意味な肥大化が防止されます。
実際、問題は何ですか?
しかし、一般に2つの問題があります。
まず、
Linq-to-Entitiesはデフォルトで標準呼び出しを変換しません。
SQLクエリの
NET 正規表現メソッド(
正規表現および
一致クラス)。 理解できる-すべてのDBMSが正規表現を計算できるわけではありません。
Linqプロバイダーを作成することもできますが、それでも少し
手間をかけたいと思います(結局、
System.Data.SQLiteには既に準備が整ったプロバイダーがあります)。
第二に、クライアント側でそのような荒野をまったく調べず、ローカルで処理を実行することも可能です。 しかし、ソリューションが失敗することは明らかです。単純なフィルタリングを実行するには、最悪の場合、テーブルからすべてのデータをダウンロードしてから、すべてのレコードを破棄する必要があります。 そして、これはクライアント側のメモリ使用量とパフォーマンスの損失です。
幸いなことに、SQLiteが役立ちます。
SQLiteには特別な
REGEXPステートメントがあり
ます 。これは、ユーザー定義の
regexp()関数の構文上のシュガーにすぎません。 デフォルトでは、この関数は実装されておらず、呼び出しによってエラーが発生するため、ユーザーはリクエストで
この関数を使用する前に
この関数を定義する必要があり
ます 。
これに基づいて構築します。 タスクは次のとおりです。
- SQLiteサーバーにregexp()関数を登録します。
- Linq-to-Entitiesが Linqクエリを変換し、 REGEXPステートメントまたはregexp()関数が使用されるようにします。
それでは始めましょう。
System.Data.SQLiteには、ユーザー定義関数を登録する機能が既にあるため、これを使用します。
関数を実装するクラスを作成します。
[SQLiteFunction(Name = "REGEXP", Arguments = 2, FuncType = FunctionType.Scalar)] public class RegExSQLiteFunction : SQLiteFunction { public static SQLiteFunctionAttribute GetAttribute() { return (SQLiteFunctionAttribute)typeof(RegExSQLiteFunction).GetCustomAttributes(typeof(SQLiteFunctionAttribute), false).Single(); } public override object Invoke(object[] args) { try {
この関数を登録するには、接続オブジェクトで対応するメソッドを呼び出します。
var connection = new SQLiteConnection(connectionString); connection.Open(); connection.BindFunction(RegExSQLiteFunction.GetAttribute(), new RegExSQLiteFunction());
それだけです! これで、サーバーによって実行される
SQLクエリで
REGEXP演算子を使用すると、実装が呼び出されます。 便利ですね。
Entity FrameworkのLinq経由でこれを呼び出す方法は?
これが「ブラックマジック」の始まりです。
Entity Frameworkモデルを作成するとき、新しい機能を説明する新しい契約を登録する必要があります。 これを行うには、たとえば、作成時にデータコンテキストクラスに次のメソッド呼び出しを追加できます。
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
合意クラス自体が関数を記述し、
Linqアダプター
System.Data.SQLiteに「理解可能」にします。
public class RegexpFunctionConvention : IStoreModelConvention<EdmModel> { public void Apply(EdmModel item, DbModel model) {
そして最後に行うことは、コンパイラの「スタブメソッド」を作成することです。そのため、
Linqプロバイダーは、その基本に基づいて、
SQLクエリで実際の関数を既に生成しています。
internal static class R { [DbFunction("CodeFirstDatabaseSchema", "regexp")] public static bool Regexp(string pattern, string input) {
生きている例
準備作業をすべて完了したら、最終的に利点を享受し、サーバー側でも実行される簡潔な
Linqクエリを作成できます(ただし、クライアント側でユーザー定義関数の実装を使用しますが、これはリクエストの処理と同じではありません)
Linq-to-Objectsをローカルで使用)。
したがって、サーバーで
IQueryableを実行すると、その利点と利点がすべて得られます。
私の短い記事が同様の問題を解決するのに役立つなら、私はうれしいです。