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。当您查询实现的对象时IQueryable<T>,例如实体框架表,该接口会公开底层 LINQ 提供程序,该提供程序知道在实体框架/SQL 中进行查询。

在查询中使用任何方法时,.NET 方法必须转换为基础数据提供程序,才能正常工作。对于 LINQ-to-Objects(不涉及数据库),这种转换是微不足道的(即不需要转换),因此您可以使用任何扩展方法。对于 LINQ-to-SQL 或 LINQ-to-Entities(如您所用),基础数据提供者必须知道如何将 CLR 方法转换为基础存储中的表示形式,例如 SQL。这是 LINQ 提供者的工作。

因此,您不能在 LINQ-to-Entities 查询中使用自定义扩展方法。为此,LINQ 提供程序需要知道如何在 SQL 中表示您的方法,而它并不知道这一点。

无论如何,实现此目的的一种常见方法是通过调用诸如ToArray()or之类的急切方法之一在底层数据提供程序中执行查询ToList(),然后使用您的自定义方法进一步细化查询。因为结果是简单的 CLR 对象,所以使用 LINQ-to-Objects,您可以使用自定义 CLR 方法。请注意,这可能会从数据库中获取许多结果。对于您的示例,您只获取一个结果,所以这无关紧要。

将上述模式应用于您的代码将如下所示:

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 回答