IQueryable 在调用 Count c# 时返回 null

发布于 2024-11-24 07:15:08 字数 2715 浏览 1 评论 0原文

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

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 对象而不是直接查询Entity.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 和 IQueryable 之间差异的一个很好的答案ObjectQuery 对象。

作为解决方案,我检查 ConferenceRole 是否为空,然后检查 contains 方法,正如你们许多人所说的那样。

I have a problem trying to get the count out of the following query:

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

Where UsersView is a class which is populated from an EF entity called users (refer to the first line in the code above)

This is the class definition for the UsersView class:

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; }
}

As I said trying to execute the line foo.Count() returns Null Exception and this might be because the ConferenceRole column allows Null in the database.

Now what I can't understand is that when I invoke the same query directly on the ObjectQuery the Count of records (i.e. invoking foo2.Count()) is returned without any exceptions.

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

Is it possible to the same query above but using the IQueryable usersView object instead?

(It is crucial for me to use the usersView object rather than directly querying the entities.users entity)

EDIT

Below is the code from the PopulateUsersView method

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();
        }
    }

Thanks

UPDATE...

Thanks guys I finally found a good answer to the difference between the IQueryable and the ObjectQuery objects.

As a solution I am checking if the ConferenceRole is null and then checking with the contains method as many of you guys have said.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沫雨熙 2024-12-01 07:15:08

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

要调试此问题:


更新

@Ryan - 谢谢用于将代码发布到 PopulateUsersView

看起来我的猜测是正确的 - 您正在执行一个查询,将整个表返回到 List - 然后您查询这个列表进一步使用Linq2Objects。

@ntziolis 为您的问题提供了一种解决方案 - 在执行 ToLower() 之前测试 null。但是,如果您唯一的要求是对非空项目列表进行计数,那么我建议您考虑更改 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

My guess is that your PopulateUsersView() method is actually executing a query and returning an IQueryable Linq-to-Objects object - while the foo2 line executes the query only in the SQL layer. If this is the case, the obviously PopulateUsersView() is going to be quite an inefficient way to perform the Count

To debug this:


Update

@Ryan - thanks for posting the code to PopulateUsersView

Looks like my guess was right - you are doing a query which gets the whole table back into a List - and its this list that you then query further using Linq2Objects.

@ntziolis has provided one solution to your problem - by testing for null before doing the ToLower(). However, if your only requirement is to Count the non-empty items list, then I recommend you look at changing the PopulateUsersView method or changing your overall design. If all you need is a Count then it would be much more efficient to ensure that the database does this work and not the C# code. This is espeically the case if the table has lots of rows - e.g. you definitely don't want to be pulling 1000s of rows back into memory from the database.


Update 2

Please do consider optimising this and not just doing a simple != null fix.

Looking at your code, there are several lines which will cause multiple sql calls:

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

Since these are called in a foreach loop, then to calculate a count of ~500 users, then you will probably make somewhere around 1501 SQL calls (although some roles and countries will hopefully be cached), returning perhaps a megabyte of data in total? All this just to calculate a single integer Count?

执笏见 2024-12-01 07:15:08

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

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

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

那么为什么它对 ObjectQuery 起作用呢?

当针对 ObjectQuery 执行查询时,LinqToSql 会将您的查询转换为正确的 sql,该 sql 不会出现空值问题,如下所示(它是示例标记 sql,只是实际查询看起来有很大不同,还使用“=”而不是检查for contains):

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

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

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

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

应该会导致与您在 UserView 中看到的完全相同的错误。

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

更新
我必须更新答案,因为我一开始就错误地查询了问题,但上述几点仍然有效。

Try to check whether ConferenceRole is null before calling a method on it:

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

This will enable you to call the count method on the user view.

So why does it work against the ObjectQuery?

When executing the query against the ObjectQuery, LinqToSql is converting your query into proper sql which does not have problems with null values, something like this (it's sample markup sql only the actual query looks much different, also '=' is used rather than checking for contains):

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

The difference to the :NET code is: It will not call a method on an object but merely call a method and pass in the value, therefore no NullReference can occur in this case.

In order to confirm this you can try to force the .NET runtime to execute the SQL prior to calling the where method, by simply adding a ToList() before the .Where()

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

This should result in the exact same error you have seen with the UserView.

And yes this will return the entire user table first, so don't use it in live code ;)

UPDATE
I had to update the answer since I c&p the wrong query in the beginning, the above points still stand though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文