2

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

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 方法进行检查。

4

2 回答 2

2

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

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

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

那么为什么它对ObjectQuery?

当对 ObjectQuery 执行查询时,LinqToSql 会将您的查询转换为正确的 sql,它不会有空值问题,像这样(它是示例标记 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.

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

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

于 2011-07-14T08:38:31.660 回答
2

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

要调试这个:


更新

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

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

@ntziolis 为您的问题提供了一种解决方案 - 通过在执行ToLower(). 但是,如果您的唯一要求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

于 2011-07-14T08:22:44.813 回答