【问题标题】:Linq throws exception on .Substring()Linq 在 .Substring() 上抛出异常
【发布时间】:2011-05-25 07:16:00
【问题描述】:

我遇到了一种情况,我需要让我的 LINQ to Entities 查询根据字符串的长度返回一个子字符串。 这是查询:

var query = (
    from f in Context.Files
    orderby f.DateAdded descending
    select new
    {
        Concerns = f.Concerns.Name,
        Sender = f.Sender.Name,
        CategoryCount = f.Categories.Count(),
        DateAdded = f.DateAdded,
        Comment = (f.Comment == null || f.Comment.Length < 5) 
            ? f.Comment : f.Comment
    }).Take(10);

所以我正在做的是获取最后 10 个添加的文件类型的实体,然后从中选择一组属性以显示在列表视图中。有些是纯字符串(关注,发件人)。 CategoryCount 返回与 File 对象关联的类别数。

但是,如果评论长于给定长度,我希望将其截断。在上面的代码中,一切正常。现在当我替换这一行时:

Comment = (f.Comment == null || f.Comment.Length < 5) 
    ? f.Comment : f.Comment

用这一行:

Comment = (f.Comment == null || f.Comment.Length < 5) 
    ? f.Comment : f.Comment.SubString(0,5)

应用程序抛出 XamlParseException (???)

在匹配指定绑定约束的类型“DocumentManager.Views.ListEntriesView”上调用构造函数引发异常

我真的不知道它为什么会那样做。 LINQ 不支持 SubString 方法吗?

希望有人可以在这里帮助我。在那之前,我会保持原样。

编辑 2(不知何故,我的第一个编辑丢失了。所以我正在重做): 根据我得到的 cmets,我将我的代码更改为这个,它现在可以工作了:

var query = App.Context.Files.OrderByDescending(File => File.DateAdded).Take(10).AsEnumerable()
            .Select(File => new
            {
                Concerns = File.Concerns.Name,
                Sender = File.Sender.Name,
                CategoryCount = File.Categories.Count(),
                DateAdded = File.DateAdded,
                Comment = (File.Comment == null || File.Comment.Length < 100) ? File.Comment : File.Comment.Substring(0, 100) + "..."
            });

我忘了提到我正在使用 SQLite。所以可能 Substring 没有在 SQLite EF Provider 中实现。

【问题讨论】:

    标签: c# .net linq entity-framework substring


    【解决方案1】:

    实际上这不是 LINQ 的错。您的模型绑定到 IQueryable,即您的数据库直接支持的例程(其他一切都会引发异常)。您应该在某个时候使用 AsEnumerable 方法来执行其他所有操作。

    在 Bill Wagner 解释 IQueryable 和 IEnumerable 之间的区别时阅读更多内容:

    http://msdn.microsoft.com/en-us/vcsharp/ff963710

    【讨论】:

    • 有趣的文章。有时间我会去看看的。无论如何感谢您的链接。
    【解决方案2】:

    我不确定,但我怀疑 Linq-to-Entities 不支持子字符串。我会将你的 Take(10) 移动到你的 select 语句之前,然后在 Take(10) 之后调用 AsEnumerable(),然后在之后有你的 select 语句。这将导致您从数据库中拉回一组文件,然后投影将在内存中完成。

    【讨论】:

    • @Brian Ball:Linq 怎么会不支持Substring?方法调用就是方法调用,不管是不是在Linq表达式里面……
    • 确实如此。了解表达式树。
    • @rsenna,您对 LINQ 的一般看法是正确的,但这不是一般的 LINQ。这是 Linq-to-Entities,该方法将转换为表达式,然后再转换为 SQL。在这种情况下,可能不支持该转换。
    • @Brian,请更新您的答案以具体说明不支持。 LINQ 作为一个整体不是问题。
    • @Gilles - 使用 Linq 提供程序时,由该提供程序的编写者决定什么是/未实现,所以如果您要更改数据库并开始使用 SQL Server,那么您的查询可能会停止工作或以不同的方式工作。希望不是,这是可能的。
    【解决方案3】:

    这似乎是 SQLite 解析器中的一个错误,因为

    • 子字符串可以正常使用 LINQ to Entities 查询 SQL Server 数据库

    • 如果查看生成的 SQL 日志,它会将其生成为 Substring SQL 函数

    • 在 SQLite 中,正确的函数是 substr,而不是 substring

    因此,它生成查询的方式存在错误。

    这是修复此错误的方法。

    在您的数据库模型中,在 EntityContainer 之前添加此代码

    <Function Name="substr" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" ReturnType="nvarchar">
      <Parameter Name="text" Type="nvarchar" Mode="In" />
      <Parameter Name="startPos" Type="int" Mode="In" />
    </Function>
    

    在您的上下文类中(在它旁边创建一个部分类),添加此代码

    [DbFunction("MyModel.Store", "substr")]
    public string SubStr(string text, int startPos) {
        return text.Substring(startPos);
    }
    

    在你的代码中,以这种方式调用 Substring

    context.SubStr(text, startpos)
    

    它现在可以正确映射到 SUBSTR 函数而不是 SUBSTRING!这就像映射一个用户定义的函数,只是我们映射到一个现有的标准函数。

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      正确 - LINQ 不支持子字符串,但是当您不幸尝试类似的事情时,从异常中并不总是很清楚。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 2018-03-27
      • 2011-07-22
      • 1970-01-01
      • 2015-08-03
      相关资源
      最近更新 更多