10

次のコードがあります。

    public QuestionDetail GetQuestionDetail(int questionId)
    {
        Question question = _questionsRepository.GetById(questionId);
        QuestionDetail questionDetail = new QuestionDetail()
        {
            QuestionId = questionId,
            Text = question.Text.FormatCode()
        };
        return questionDetail;
    }

これを次のものに置き換えました:

    public QuestionDetail GetQuestionDetail(int questionId)
    {
        var questions = _questionsRepository
            .GetAll()
            .Include(q => q.Answers)
            .Select(m => new QuestionDetail
            {
                QuestionId = m.QuestionId,
                Text = m.Text.FormatCode()
            })
            .FirstOrDefault();

        return questions;
    }

次のエラーメッセージが表示されます。

LINQ to Entities does not recognize the method 'System.String FormatCode(System.String)' 
method, and this method cannot be translated into a store expression.

ここに私の FormatCode() があります

public static class CodeDisplay {

    public static string FormatCode(this string content)
    {
        var data1 = content
            .Split(new[] { "<pre>", "</pre>" }, StringSplitOptions.None);
        var data2 = data1
            .Select((s, index) =>
            {
                string s1 = index % 2 == 1 ? string.Format("{0}{2}{1}",
                    "<table class='code'>", "</table>", SplitJoin(s)) : s;
                return s1;
            });
        var data3 = data2.Where(s => !string.IsNullOrEmpty(s));
        var data4 = string.Join("\n", data3);
        return data4;
    }

    private static string SplitJoin(string content)
    {
        IEnumerable<String> code =
            content.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
                .Select((line, index) =>
                    string.Format("<tr><td>{0}</td><td><pre><code>{1}</code></pre></td></tr>\n",
                    (index + 1).ToString("D2"), HttpUtility.HtmlEncode(line)));
        return string.Join("", code) + "\n";
    }


}
4

3 に答える 3

15

簡単な回答: はい、LINQ クエリ内でカスタム拡張メソッドを使用できますが、基になるデータ プロバイダーが実行方法を知らない拡張メソッドを使用することはできません。

LINQ は Language-Integrated-Query の略で、C# の言語機能です。LINQ クエリでは、任意の .NET メソッドを使用できます。LINQ 自体は、基礎となるデータ ストアを認識しません。その詳細は、IEnumerable<T>またはIQueryable<T>インターフェイスを介して LINQ に公開されます。Entity Framework テーブルなど、を実装するオブジェクトに対してクエリを実行する場合IQueryable<T>、インターフェイスは、Entity Framework / SQL でのクエリを認識している基になる LINQ プロバイダーを公開します。

クエリで任意のメソッドを使用する場合、これが機能するためには、.NET メソッドに基になるデータ プロバイダーへの変換が必要です。LINQ-to-Objects (データベースが含まれていない) の場合、この変換は簡単です (つまり、変換は必要ありません)。そのため、任意の拡張メソッドを使用できます。LINQ-to-SQL または LINQ-to-Entities (使用している) の場合、基になるデータ プロバイダーは、CLR メソッドを基になるストレージ (SQL など) の表現に変換する方法を認識している必要があります。これは、LINQ プロバイダーの仕事です。

したがって、LINQ-to-Entities クエリ内でカスタム拡張メソッドを使用することはできません。それが機能するためには、LINQ プロバイダーは SQL でメソッドを表す方法を知る必要がありますが、それはわかりません。

ToArray()とにかくこれを実現する一般的な方法は、またはなどの熱心なメソッドのいずれかを呼び出して、基になるデータプロバイダーでクエリを実行し、ToList()その後カスタムメソッドでクエリをさらに改良することです。結果は単純な CLR オブジェクトであるため、LINQ-to-Objects が使用され、カスタム CLR メソッドを使用できます。これにより、データベースから多くの結果が取得される可能性があることに注意してください。あなたの例では、結果を 1 つだけ取得しているため、これは問題ではありません。

上記のパターンをコードに適用すると、次のようになります。

public QuestionDetail GetQuestionDetail(int questionId)
{
    var questions = _questionsRepository
        .GetAll()
        .Include(q => q.Answers)
        .Take(1)   // Constrain to one result fetched from DB
        .ToArray() // Invoke query in DB
        .Select(m => new QuestionDetail
        {
            QuestionId = m.QuestionId,
            Text = m.Text.FormatCode()
        })
        .FirstOrDefault();

    return questions;
}
于 2013-09-15T14:51:57.690 に答える
2

LINQ to Entities で FormatCode() メソッドを実行しようとする代わりに、ADO.NET プロバイダーがそれを SQL に変換する方法を認識していないために失敗します。次のように LINQ to Entities として実行します。

var questionTmp = _questionsRepository
        .GetAll()
        //.Include(q => q.Answers) // commented out since you aren't selecting this
        .Select(m => new // Anonymous type
        {
            QuestionId = m.QuestionId,
            Text = m.Text, // raw data to be used as input for in-memory processing
        })
        .FirstOrDefault(); // or use .ToList(); if you want multiple results

次に、次のように、結果に対してメモリ内でメソッドを実行します。

// For one
var question = new QuestionDetail
{
    QuestionId = questionTmp.QuestionId,
    Text = questionTmp.Text.FormatCode(),
};

// Or for many
var questions = questionsTmp.Select(q =>
    new QuestionDetail
    {
        QuestionId = q.QuestionId,
        Text = q.Text.FormatCode(),
    });

return question; // or questions;
于 2013-09-15T15:17:51.470 に答える
0

あなたは試すことができます

public QuestionDetail GetQuestionDetail(int questionId)
{
    Question question = _questionsRepository.GetById(questionId).ToList();
    QuestionDetail questionDetail = new QuestionDetail()
    {
        QuestionId = questionId,
        Text = question.Text.FormatCode()
    };
    return questionDetail;
}

これはquestionメモリ内で具体化され、必要なフォーマットを適用できるようになります。

于 2013-09-15T14:52:24.463 に答える