IQueryable возвращает null при вызове Count c # - PullRequest
2 голосов
/ 14 июля 2011

У меня проблема при попытке получить счетчик из следующего запроса:

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

Где UsersView - это класс, который заполняется из сущности 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)

EDIT

Ниже приведен код из метода 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 нулевым, а затем проверяю с помощью метода contains, как сказали многие из вас.

Ответы [ 2 ]

2 голосов
/ 14 июля 2011

Попробуйте проверить, является ли ConferenceRole нулевым, перед вызовом метода для него:

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

Это позволит вам вызвать метод подсчета в представлении пользователя.

Так почемуработает ли он против ObjectQuery?

При выполнении запроса к ObjectQuery LinqToSql преобразует ваш запрос в правильный sql, у которого нет проблем с нулевыми значениями, что-то вроде этого (это пример разметки sql толькофактический запрос выглядит очень по-разному, также используется «=» вместо проверки на наличие содержимого):

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

Разница в коде: NET: Он не вызовет метод объекта, нопросто вызовите метод и передайте значение , поэтому в этом случае не может возникнуть NullReference.

Чтобы подтвердить это, вы можете попытаться заставить среду выполнения .NET выполнить SQL перед вызовомгде метод, просто добавив ToList() перед .Where()

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

Это должно привести к той же самой ошибке, которую вы видели с UserView.

И даэто сначала вернет всю пользовательскую таблицу, так что не используйте ее в действующем коде;)

ОБНОВЛЕНИЕ
Мне пришлось обновить ответ, так как я ввел неправильный запрос вначало, вышеупомянутые пункты все еще стоят, хотя.

2 голосов
/ 14 июля 2011

My думаю, заключается в том, что ваш метод PopulateUsersView() фактически выполняет запрос и возвращает объект IQueryable Linq-to-Objects, в то время как строка foo2 выполняет запрос только на уровне SQL.Если это так, то очевидно, что PopulateUsersView() будет довольно неэффективным способом выполнения Count

. Для отладки:

  • Вы можете опубликовать некоторый код изPopulateUsersView()?
  • Можете ли вы попробовать запустить оба набора кода с помощью поставщика трассировки EF, чтобы увидеть, что выполняется в SQL?(см. http://code.msdn.microsoft.com/EFProviderWrappers)

Обновление

@ Райан - спасибо за отправку кода в PopulateUsersView

Похоже, мое предположение было правильным - вывыполняем запрос, который возвращает всю таблицу обратно в List - и этот список, который вы затем запрашиваете с помощью Linq2Objects.

@ ntziolis предоставил одно решение вашей проблемы - путем проверки на нулевое значение перед выполнениемToLower(). Однако, если ваше единственное требование - Count список непустых элементов, то я рекомендую вам взглянуть на изменение метода PopulateUsersView или изменение вашего общего дизайна. Если все, что вам нужно, это Countтогда было бы гораздо эффективнее убедиться, что база данных выполняет эту работу, а не код на C #, особенно в том случае, если в таблице много строк - например, вы определенно не хотите вытягивать тысячи строк обратно в памятьиз базы данных.


Обновление 2

Пожалуйста, рассмотрите возможность оптимизации этого, а не просто делайте простое != null исправление.

Глядя на ваш код, естьsНесколько линий, которые будут вызывать несколько вызовов SQL:

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

Так как этиВызываются в цикле foreach, а затем для подсчета количества ~ 500 пользователей, тогда вы, вероятно, сделаете где-то около 1501 вызова SQL (хотя некоторые роли и страны, будем надеяться, будут кэшироваться), возвращая, возможно, всего мегабайт данных?Все это только для вычисления одного целого числа Count?

...