IQueryable在调用Count c#时返回null

时间:2011-07-14 07:59:44

标签: c# linq iqueryable objectquery

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

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 Exception,这可能是因为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();
        }
    }

由于

... UPDATE

谢谢大家,我终于找到了IQueryable和ObjectQuery对象之间差异的一个很好的答案。

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

2 个答案:

答案 0 :(得分:2)

我的猜测是您的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调用中进行调用(尽管某些角色和国家有望被缓存),可能会返回一兆字节的数据总共?所有这些只是为了计算一个整数Count

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

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

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

这会导致您在UserView看到完全相同的错误。

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

<强>更新
我不得不更新答案,因为我在开始时提出了错误的查询,但上述观点仍然存在。