Член указанного типа «UsersCount» не поддерживается в LINQ to Entities.

У меня есть такая сущность POCO

  public class Product : Entity

 {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }
        [Required]
        [MaxLength(50)]
        public string Name { get; set; }              
        public virtual ICollection<Order> Orders { get; set; }

        [NotMapped]
        public int UsersCount
        {
            get
            {
                return  Orders.Count(); 
            }
        }

}

Метод доступа к продукту

 public IQueryable<Product> GetAll()
        {
            return _context.Product.Include(I=>I.Orders);          
        }    

Когда я загружаю все продукты в View

      var model = _productService.GetAll().Select(p => new AdminProductViewModel
        {
            Active = p.Active,
            Id = p.Id,
            Name = p.Name,
            UsersCount = p.UsersCount
        }).ToList();

я получаю исключение

Член указанного типа «UsersCount» не поддерживается в LINQ to Entities.

Я действительно не могу понять, почему Linq to Entity дает исключение. Может кто объяснит что не так?

Мы также используем вычисляемые поля в другом приложении, подобном этому

 public class User : Entity
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }               
        [Required]
        [MaxLength(50)]
        public string Email { get; set; }
        [MaxLength(50)]
        public string FirstName { get; set; }
        [MaxLength(50)]
        public string LastName { get; set; }

        public virtual ICollection<Order> Orders { get; set; }
        public virtual ICollection<Statistic> Statistics { get; set; }

        [NotMapped]
        public bool Active
        {
            get
            {
                return Orders.Any(c => c.Active && (c.TransactionType == TransactionType.Order || c.TransactionType == TransactionType.Subscription));
            }
        }

        [NotMapped]
        public int CreditsLeft
        {
            get
            {
                return Orders.Where(w => w.Active).Sum(p => p.Credits != null ? p.Credits.Value : 0);
            }
        }
}

    public User Get(int id)
    {
        return _context.User.FirstOrDefault(u => u.Id == id);
    }

var user = _userService.Get(_authUser.Id);

 var model = new UserViewModel
            {
                Active = user.Active,
                FullName = user.FullName,
                Email = user.Email,
            };

и нет проблем, EF6 не дает никаких исключений, хотя у него также есть два вычисляемых поля User.Active и User.CreditsLeft


person Tomas    schedule 16.09.2014    source источник
comment
См. остальную часть сообщения об исключении. Это говорит вам, что происходит.   -  person Gert Arnold    schedule 16.09.2014
comment
Поддерживаются только инициализаторы, члены сущностей и свойства навигации сущностей. И что? UsersCount содержит поддерживаемый код linq-to-Entity Orders.Count(); Текст исключения не дает мне логического объяснения, почему я получаю исключение. Я не использую код, который нельзя преобразовать в настоящий SQL.   -  person Tomas    schedule 16.09.2014
comment
Да, да, хотя это может показаться неочевидным. EF видит только UsersCount и пытается перевести это в SQL. Он не просматривает членов, с которыми сталкивается в запросах LINQ. Кроме того, UsersCount не отображается, поэтому, если EF может сопоставить его со столбцом или функцией SQL, вы не разрешили это сделать. Вам придется написать Orders.Count() непосредственно в операторе LINQ.   -  person Gert Arnold    schedule 16.09.2014
comment
Я разместил новый код из нашего другого приложения, он вычислил, и поля не отображаются в базу данных, и все работает нормально. Может быть, вы могли бы дать мне представление, почему в одних и тех же случаях это работает, а иногда нет.   -  person Tomas    schedule 16.09.2014
comment
Там другие данные оцениваются, когда пользователь находится в памяти, а не когда они извлекаются из базы данных.   -  person Gert Arnold    schedule 16.09.2014
comment
@GertArnold Добавьте свои комментарии в качестве ответа, я проголосую   -  person The One    schedule 16.09.2014


Ответы (2)


Имейте в виду, что LINQ to Entities пытается преобразовать каждую инструкцию LINQ в SQL. Ваше заявление...

var model = _productService.GetAll().Select(p => new AdminProductViewModel...

...является методом расширения LINQ (Select) против IQueryable (_productService.GetAll()). Как показано в документации, этот метод принимает Expression как аргумент.

Вы можете видеть выражение в виде дерева токенов, которые выражают задачу, которую оно должно выполнить. Проще говоря, поставщик LINQ — это словарь токенов на «языке выражений» для токенов на каком-то другом языке, в данном случае SQL. Весь оператор переводится в SQL и выполняется базой данных. Среда выполнения .Net только отправляет оператор и обрабатывает возвращенный результат.

Проверка исходного кода EF показывает, что многие токены жестко закодированы: все ключевые слова SQL, ряд встроенных («канонических») функций (например, DATEDIFF) и набор методов .Net. Другие маркеры добавляются путем сопоставления свойств объекта со столбцами базы данных. Недавно ToString() был добавлен в часть словаря .Net. В EF6 мы можем написать...

_context.Product.Select(p => p.Id.ToString())

До этого это подняло бы печально известную

LINQ to Entities не распознает метод System.String ToString().

Ваше исключение имеет ту же причину, но относится к членам, а не к методам. p.UsersCount нет в словаре, потому что он не отображается.

Мы также используем вычисляемые поля в другом приложении, подобном этому

Здесь User было получено из базы данных и материализовано как объект C#. Теперь, когда вы обращаетесь к его свойствам, это просто работает код .Net. Здесь не происходит преобразования SQL. Ну... это, вероятно, вызывает ленивую загрузку (заказов и кредитов), но акт доступа к свойству не происходит в контексте выражения.

Точно так же вы можете получить доступ к UsersCount, если у вас есть объект Product. Если вы хотите, чтобы база данных выполняла тяжелую работу по подсчету заказов, вам придется использовать выражение в операторе LINQ:

var model = _productService.GetAll().Select(p => new AdminProductViewModel
    {
        Active = p.Active,
        Id = p.Id,
        Name = p.Name,
        UsersCount = p.Orders.Count()
    }).ToList();
person Gert Arnold    schedule 16.09.2014

Попробуйте это вместо этого:

   var model = _productService.GetAll().Select(p => new AdminProductViewModel
   {
      Active = p.Active,
      Id = p.Id,
      Name = p.Name,
      UsersCount = p.Orders.Count()
   }).ToList();

Linq to Entities не может перевести свойство Product.UsersCount в sql, поэтому выдает сообщение об ошибке

Член указанного типа «UsersCount» не поддерживается в LINQ to Entities.

person Colin    schedule 16.09.2014