我可以在LINQ查询中使用扩展方法吗?

时间:2013-09-15 14:47:47

标签: c# linq-to-entities

我有以下代码:

    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";
    }


}

3 个答案:

答案 0 :(得分:11)

简短回答:是的,您可以在LINQ查询中使用自定义扩展方法 - 但是您不能使用基础数据提供程序不知道如何执行的扩展方法。

LINQ代表Language-Integrated-Query,是C#中的语言功能。您可以在LINQ查询中使用任何.NET方法。 LINQ本身并不了解底层数据存储,其详细信息通过IEnumerable<T>IQueryable<T>接口向LINQ公开。当您查询实现IQueryable<T>的对象(例如实体框架表)时,该接口将公开底层LINQ提供程序,该提供程序了解有关在Entity Framework / SQL中查询的信息。

在查询中使用任何方法时,.NET方法必须转换为基础数据提供程序才能生效。对于LINQ到对象(不涉及数据库),此转换很简单(即不需要转换),因此您可以使用任何扩展方法。对于LINQ-to-SQL或LINQ-to-Entities(正如您所使用的),底层数据提供程序必须知道如何将CLR方法转换为底层存储中的表示,例如SQL。这是LINQ提供商的工作。

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

无论如何,实现此目的的一种常见方法是通过调用其中一个急切方法(如ToArray()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;
}

答案 1 :(得分: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;

答案 2 :(得分: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,您将能够应用所需的格式。