【问题标题】:Custom Method in LINQ QueryLINQ 查询中的自定义方法
【发布时间】:2014-08-04 05:09:01
【问题描述】:

我总结了自己在 LINQ to EF 查询中摸索自定义方法的倒霉蛋。我浏览了网络,试图检测一种模式,使自定义方法对 LINQ 友好,虽然每个消息来源都说该方法必须是 translatable into a T-SQL query,但应用程序似乎非常多样化。所以,我将在这里发布我的代码,希望慷慨的 SO 居民能告诉我我做错了什么以及为什么。

代码

    public IEnumerable<WordIndexModel> GetWordIndex(int transid)
    {
        return (from trindex in context.transIndexes
                let trueWord = IsWord(trindex)
                join trans in context.Transcripts on trindex.transLineUID equals trans.UID
                group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
                orderby grouped.Key.word
                where grouped.Key.transID == transid
                select new WordIndexModel
                {
                    Word = TrueWord,
                    Instances = grouped.Select(test => test.trans).Distinct()
                });
    
    }

    public string IsWord(transIndex trindex)
    {
        Match m = Regex.Match(trindex.word, @"^[a-z]+(\w*[-]*)*",
                              RegexOptions.IgnoreCase);
        return m.Value;

    }

通过上面的代码,我访问了一个表transIndex,它本质上是一个从各种用户文档中挑选出来的单词索引。问题是并非所有条目实际上都是单词。 Nubers,甚至下划线,例如___________,,也会被保存。

问题

我只想保留我的自定义方法IsWord 返回的单词(目前我还没有真正开发出解析机制)。但正如IsWord 函数所示,它将返回string

所以,我使用let 将我的自定义方法引入到查询中并将其用作分组参数,可以在我的对象中选择。执行后,我得到了无所不知的:

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

我还需要确保只返回符合IsWord 条件的记录。

有什么想法吗?

【问题讨论】:

  • 你可以先枚举(这通常是个坏主意),或者在 IsWord 中解释你想做什么。有了这个,有人可能会帮助你(或解释为什么这是不可能的),而无需......
  • @RaphaëlAlthaus 查看编辑后的IsWord 方法

标签: c# sql linq entity-framework let


【解决方案1】:

这是说它不理解您的 IsWord 方法如何将其转换为 SQL。

坦率地说,无论如何它并没有多大作用,为什么不将其替换为

return (from trindex in context.transIndexes
                let trueWord = trindex.word
                join trans in context.Transcripts on trindex.transLineUID equals trans.UID
                group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
                orderby grouped.Key.word
                where grouped.Key.transID == transid
                select new WordIndexModel
                {
                    Word = TrueWord,
                    Instances = grouped.Select(test => test.trans).Distinct()
                });

EF 可以将哪些方法翻译成 SQL,我不能给你一个列表,但它永远无法翻译你编写的直接方法。但它们是一些内置的,它可以理解,例如MyArray.Contains(x),它可以把它变成类似

...
WHERE Field IN (ArrItem1,ArrItem2,ArrItem3)

如果你想编写一个 linq 兼容的方法,那么你需要创建一个 EF 可以理解并转化为 SQL 的表达式树。

这是让我有点想不开的地方,但这篇文章可能对http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx有所帮助。

【讨论】:

  • 感谢您的尝试,但您没有阅读我在帖子中提供的解释。目前IsWord 没有做任何事情,但如果我能找到可行的解决方案,它会检查trindex.word 以查看它是否是符合我规格的word。所以,我需要IsWord
  • 然后你进入了使用表达式树创建动态方法的可怕世界。请参阅我发布的链接。然后您进入使用表达式树创建动态方法的可怕世界。请参阅我发布的链接。此外,如果记录集不大,则将全部内容放入内存并按照 Believe204 的回答调用您的方法是可行的。然而,记录集越大,效率就越低。
  • 我对表达式树的了解恐怕不够,给你建议如何将你的方法变成使用表达式的动态linq方法。
  • 很好的回应。尽管@Believe2014 提供了一个不错的工作答案,但您确实回答了我的问题,即忘记自定义方法...
  • @Seabiscuit 使用表达式可能是可行的,或者至少是一些等效的。但是您不能将直接的 c# 方法传递给使用 EF 或任何其他 linq 提供程序的 linq 查询,这些提供程序会关闭并将表达式转换为 SQL 之类的东西。它根本不知道如何将您的 c# 转换为 SQL。
【解决方案2】:

如果返回的坏记录比例不大,可以考虑先枚举结果集,再进行处理/过滤?

var query = (from trindex in context.transIndexes
             ...
             select new WordIndexModel
                {
                    Word,
                    Instances = grouped.Select(test => test.trans).Distinct()
                });
var result = query.ToList().Where(word => IsTrueWord(word));

return result;

如果记录数太多而无法枚举,请考虑在视图或存储过程中进行检查。这将有助于提高速度并保持代码整洁。

当然,使用存储过程在可重用性和可维护性方面存在缺点(因为没有重构工具)。

另外,请查看另一个与此类似的答案:https://stackoverflow.com/a/10485624/3481183

【讨论】:

  • 我一直在考虑处理集合视图中的列表。
  • 要么是SqlServer 必须做记录过滤,要么是应用服务器有。如果选择 SqlServer 来执行该任务,它将节省网络延迟和带宽。但是如果一切都以这种方式完成,它将使Sql成为瓶颈。找到一个良好的平衡是关键。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多