我有以下代码:
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";
}
}
答案 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
,您将能够应用所需的格式。