我在尝试从以下查询中获取计数时遇到问题:
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方法进行检查,正如你们许多人所说的那样。
答案 0 :(得分:2)
我的猜测是您的PopulateUsersView()
方法实际执行查询并返回IQueryable Linq-to-Objects对象 - 而foo2
行仅执行查询SQL层。如果是这种情况,显然PopulateUsersView()
将是执行Count
调试:
PopulateUsersView()
的一些代码吗?更新
@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
看到完全相同的错误。
是的,这将首先返回整个用户表,所以不要在实时代码中使用它;)
<强>更新强>
我不得不更新答案,因为我在开始时提出了错误的查询,但上述观点仍然存在。