Entity Framework - ワイルドカード文字を使った文字列検索

Entity FrameworkでstringのContainsメソッド、StartsWithメソッド、EndsWithメソッドを使って文字列を検索すると、LIKE演算子で「%」を使ったselect文が作成されます。

MSDNとかのドキュメントっぽいものは見つけられませんでしたが、適当なエンティティとDBコンテキストを用意して実行されるクエリを確認してみます。

// エンティティ
class Item {
    public int Id { get; set; }
    public string Name { get; set; }
}

// DBコンテキスト
class AppDbContext : DbContext {
    // 略
}

Containsメソッドで部分一致

Containsメソッドを使うと部分一致になります。実行されるクエリを見てみるとキーワードの前後に「%」がついてLIKE演算子が使われていることが確認できます。

// Containsメソッドを使った検索
using (var dbContext = new AppDbContext()) {
    var items = dbContext.Items.Where(item => item.Name.Contains("a")).ToList();
}

// 実行されるクエリ
/*
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Item] AS [Extent1]
    WHERE [Extent1].[Name] LIKE N'%a%'
*/

StartsWithメソッドで前方一致、EndsWithメソッドで後方一致

StartsWithメソッド、EndsWithメソッドも同様ですが、念のため実行されるクエリを確認してみます。

// StartsWithメソッドを使った検索
using (var dbContext = new AppDbContext()) {
    var items = dbContext.Items.Where(item => item.Name.StartsWith("a")).ToList();
}

// 実行されるクエリ
/*
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Item] AS [Extent1]
    WHERE [Extent1].[Name] LIKE N'a%'
*/

// EndsWithメソッドを使った検索
using (var dbContext = new AppDbContext()) {
    var items = dbContext.Items.Where(item => item.Name.EndsWith("a")).ToList();
}

// 実行されるクエリ
/*
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Item] AS [Extent1]
    WHERE [Extent1].[Name] LIKE N'%a'
*/

とここまでは前振りようなもので今回気になったのはここからです。

他のワイルドカード文字([ ]など)を使ったパターン検索

SQL ServerのLIKE演算子では「%」の他に「[ ]」などのワイルドカード文字を使った簡単な正規表現のようなパターン検索もできます。

ただ上記のContainsメソッドなどを使うとワイルドカード文字がエスケープされてしまいます。

// aかbかcで始まる文字列を検索
using (var dbContext = new AppDbContext()) {
    var items = dbContext.Items.Where(item => item.Name.Contains("[a-c]%")).ToList();
}

// 実行されるクエリ
// 「[」と「%」がエスケープされてしまう
/*
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Item] AS [Extent1]
    WHERE [Extent1].[Name] LIKE N'%~[a-c]~%%' ESCAPE N'~'
*/

どうしたらいいか調べたところ、PATINDEX関数を使うとできるようです。

// aかbかcで始まる文字列を検索
using (var dbContext = new AppDbContext()) {
    var items = dbContext.Items.Where(item => SqlFunctions.PatIndex("[a-c]%", item.Name) > 0).ToList();
}

// 実行されるクエリ
/*
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Item] AS [Extent1]
    WHERE ( CAST(PATINDEX(N'[a-c]%', [Extent1].[Name]) AS int)) > 0
*/

正直なところLIKEを使ったクエリを書いてしまった方がわかりやすいのかなと思ったりしますが・・・。

SqlFunctionsクラスでSQL Serverの関数を使えるということがわかったので調べてよかったかなと。

参考