【问题标题】:IQueryable returns null on invoking Count c#IQueryable 在调用 Count c# 时返回 null
【发布时间】:2023-03-07 02:39:01
【问题描述】:

我在尝试从以下查询中获取计数时遇到问题:

var usersView = PopulateUsersView(); //usersView is an IQueryable object
var foo = usersView.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

其中 UsersView 是一个从名为 users 的 EF 实体填充的类(请参阅上面代码中的第一行)

这是 UsersView 类的类定义:

public class UsersView
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string PostCode { get; set; }
    public string CountryName { get; set; }
    public string WorkPlaceName { get; set; }
    public string Gender { get; set; }
    public string EMail { get; set; }
    public string Company { get; set; }
    public string RoleName { get; set; }
    public string ConferenceRole { get; set; }
}

正如我所说,尝试执行 foo.Count() 行返回 Null 异常,这可能是因为 ConferenceRole 列允许数据库中的 Null。

现在我无法理解的是,当我直接在 ObjectQuery 上调用相同的查询时,将返回记录计数(即调用 foo2.Count()),没有任何异常。

var foo2 = entities.users.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

是否可以使用 IQueryable usersView 对象代替上面的相同查询?

(使用 usersView 对象而不是直接查询entities.users 实体对我来说至关重要)

编辑

下面是 PopulateUsersView 方法的代码

private IQueryable<UsersView> PopulateUsersView()
    {
        using (EBCPRegEntities entities = new EBCPRegEntities())
        {
            var users = entities.users.ToList();
            List<UsersView> userViews = new List<UsersView>();
            foreach (user u in users)
            {
                userViews.Add(new UsersView()
                {
                    UserId = u.UserId,
                    Title = u.Title,
                    Name = u.Name,
                    Surname = u.Surname,
                    Street1 = u.Street1,
                    Street2 = u.Street2,
                    City = u.City,
                    PostCode = u.Post_Code,
                    CountryName = u.country.Name,
                    WorkPlaceName = u.workplace.Name,
                    Gender = u.Gender,
                    EMail = u.E_Mail,
                    Company = u.Company,
                    RoleName = u.roles.FirstOrDefault().Name,
                    ConferenceRole = u.ConferenceRole
                });
            }
            return userViews.AsQueryable();
        }
    }

谢谢

更新...

谢谢大家,我终于找到了一个很好的答案来解决 IQueryable 和 ObjectQuery 对象之间的区别。

作为一种解决方案,我正在检查 ConferenceRole 是否为空,然后像你们中的许多人所说的那样使用 contains 方法进行检查。

【问题讨论】:

  • 我建议你对这两种方法做一个比较:ConferenceRole在数据库中为NULL的实体的内容是什么?
  • 它是字符串,默认在数据库中创建为空。
  • 这不是我要问的。显然,当您通过 usersView 访问 ConferenceRole 时,ConferenceRole 为 null,但当您直接从数据库访问时,则不会。那么它是什么呢?
  • 你的意思是使用Except扩展方法?在 IQueryable (usersView) 对象返回的所有记录与 ObjectQuery? 的过滤结果集之间
  • 修改查询以添加null 检查usersView.Where(fields =&gt; fields.ConferenceRole != null &amp;&amp; ....);

标签: c# linq iqueryable objectquery


【解决方案1】:

我的猜测是您的PopulateUsersView() 方法实际上正在执行一个查询并返回一个IQueryable Linq-to-Objects 对象——而foo2 行仅在SQL 层中执行查询。如果是这种情况,显然PopulateUsersView() 将是执行Count 的一种非常低效的方式

要调试这个:


更新

@Ryan - 感谢您将代码发布到 PopulateUsersView

看来我的猜测是正确的 - 您正在执行一个查询,将整个表重新放入 List - 然后您可以使用 Linq2Objects 进一步查询此列表。

@ntziolis 为您的问题提供了一种解决方案 - 在执行 ToLower() 之前测试 null。但是,如果您的唯一要求是 Count 非空项目列表,那么我建议您查看更改 PopulateUsersView 方法或更改您的整体设计。如果您只需要一个Count,那么确保数据库完成这项工作而不是 C# 代码会更有效率。如果表有很多行,尤其是这种情况 - 例如您绝对不想将 1000 行从数据库中拉回内存。


更新 2

请考虑对此进行优化,而不仅仅是做一个简单的!= null 修复。

查看您的代码,有几行会导致多次 sql 调用:

  • CountryName = u.country.Name
  • WorkPlaceName = u.workplace.Name
  • RoleName = u.roles.FirstOrDefault().Name

由于这些是在 foreach 循环中调用的,因此要计算约 500 个用户的计数,那么您可能会进行大约 1501 次 SQL 调用(尽管希望缓存一些角色和国家/地区),返回可能是 1 兆字节的数据总共?这一切只是为了计算一个整数Count

【讨论】:

  • @Ryan - 谢谢 - 我已经为我的答案添加了更新。如果用户将是一个大表,那么不要只在此处应用简单的!= null 修复 - 这会损害您的性能。
  • 用户数最多为 500,所以不算多。感谢您的提示
  • @Ryan - 除非您出于其他原因使用该列表,否则即使有 500 个用户,您仍应将此 Count 推送到数据库 - 它会优化此 Count 查询,并且能够返回答案的速度比大获取快得多,并且 c# 代码将起作用。如果可以的话,当数据库只能返回一个整数时,尽量避免拉回 500kB 的数据并执行 500 次 C# 代码循环。
  • 添加了一个“更新 2”来解释我的性能问题 - 保证我现在会停止 :)
  • 我喜欢你的想法 Stuart....但是这个事件不仅仅是为了一个 Count 调用,而是它会被传递给一个小型框架,以便 jqGrid 使用动态使用的数据填充网格表达式。
【解决方案2】:

在调用ConferenceRole的方法之前尝试检查它是否为空:

var foo = usersView.Where(fields => fields.ConferenceRole != null 
    && fields.ConferenceRole.ToLower().Contains("role"));

这将使您能够在用户视图上调用 count 方法。

那么为什么它对 ObjectQuery 有效?

当对 ObjectQuery 执行查询时,LinqToSql 会将您的查询转换为正确的 sql,它不会出现 null 值的问题,就像这样(它是示例标记 sql,只有实际查询看起来有很大不同,也使用了 '='而不是检查包含):

SELECT COUNT(*) from USERS U WHERE TOLOWER(U.CONFERENCEROLE) = 'role'

与 :NET 代码的区别在于:它不会调用对象的方法,而只是调用方法并传入值,因此在这种情况下不会发生 NullReference。 p>

为了确认这一点,您可以尝试强制 .NET 运行时在调用 where 方法之前执行 SQL,只需在 .Where() 之前添加 ToList() 即可

var foo2 = entities.users.ToList()
    .Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

这应该会导致与UserView 完全相同的错误。

是的,这将首先返回整个用户表,所以不要在实时代码中使用它;)

更新
我不得不更新答案,因为我一开始就 c&p 了错误的查询,但上述几点仍然有效。

【讨论】:

    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 2017-01-24
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多