Сущность не может быть построена в запросе LINQ to Entities

Существует тип объекта, называемый продуктом, который создается инфраструктурой entity. Я написал этот запрос

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}

В приведенном ниже коде появляется следующая ошибка:

"Объект или сложный тип Shop.Product не может быть построен в Запрос LINQ to Entities"

var products = productRepository.GetProducts(1).Tolist();

Но когда я использую select p вместо select new Product { Name = p.Name};, он работает правильно.

Как я могу создать пользовательский раздел выбора?

Ответ 1

Вы не можете (и не должны) проектировать на сопоставленную сущность. Вы можете, однако, проецировать на анонимный тип или на DTO:

public class ProductDTO
{
    public string Name { get; set; }
    // Other field you may need from the Product entity
}

И ваш метод вернет список DTO.

public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}

Ответ 2

Вы можете проецировать в анонимный тип, а затем от него к типу модели

public IEnumerable<Product> GetProducts(int categoryID)
{
    return (from p in Context.Set<Product>()
            where p.CategoryID == categoryID
            select new { Name = p.Name }).ToList()
           .Select(x => new Product { Name = x.Name });
}

Изменить. Я собираюсь быть более конкретным, так как этот вопрос получил много внимания.

Вы не можете напрямую проецироваться в тип модели (ограничение EF), поэтому нет никакого способа обойти это. Единственный способ - проецировать на анонимный тип (1-я итерация), а затем на тип модели (2-я итерация).

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

Я никогда не понимал, почему это невозможно, и ответы на эту тему не дают против этого серьезных оснований (в основном, о частично загруженных данных). Правильно, что в частично загруженном состоянии сущность не может быть обновлена, но тогда этот объект будет отсоединен, поэтому случайные попытки сохранить их не будут возможны.

Рассмотрим метод, который я использовал выше: в результате у нас все еще есть частично загруженная модель. Этот объект отсоединен.

Рассмотрим возможный код (желаемый):

return (from p in Context.Set<Product>()
        where p.CategoryID == categoryID
        select new Product { Name = p.Name }).AsNoTracking().ToList();

Это также может привести к списку отдельных объектов, поэтому нам не нужно будет делать две итерации. Компилятор был бы умен, чтобы увидеть, что AsNoTracking() используется, что приведет к отсоединенным объектам, чтобы оно могло позволить нам это сделать. Если, однако, AsNoTracking() был опущен, он мог бы сделать то же исключение, что и сейчас, чтобы предупредить нас, что нам нужно быть достаточно конкретным относительно желаемого результата.

Ответ 3

Есть другой способ, которым я нашел работу, вам нужно создать класс, который происходит из вашего класса Product и использовать его. Например:

public class PseudoProduct : Product { }

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new PseudoProduct() { Name = p.Name};
}

Не уверен, что это разрешено, но оно работает.

Ответ 4

Вот один из способов сделать это, не объявляя дополнительный класс:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select new { Name = p.Name };
    var products = query.ToList().Select(r => new Product
    {
        Name = r.Name;
    }).ToList();

    return products;
}

Однако это нужно использовать, только если вы хотите объединить несколько объектов в одном объекте. Вышеупомянутая функциональность (простое сопоставление продукта с продуктом) выполняется следующим образом:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select p;
    var products = query.ToList();

    return products;
}

Ответ 5

Еще один простой способ:)

public IQueryable<Product> GetProducts(int categoryID)
{
    var productList = db.Products
        .Where(p => p.CategoryID == categoryID)
        .Select(item => 
            new Product
            {
                Name = item.Name
            })
        .ToList()
        .AsQueryable(); // actually it not useful after "ToList()" :D

    return productList;
}

Ответ 6

Вы можете использовать это, и он должен работать. > Вы должны использовать toList, прежде чем создавать новый список, используя select:

db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList(); 

Ответ 7

добавить только AsEnumerable():

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}

Ответ 8

В ответ на другой вопрос, который был отмечен как дубликат (см. здесь), я понял быстрое и простое решение, основанное на ответе Сорена:

data.Tasks.AddRange(
    data.Task.AsEnumerable().Select(t => new Task{
        creator_id   = t.ID,
        start_date   = t.Incident.DateOpened,
        end_date     = t.Incident.DateCLosed,
        product_code = t.Incident.ProductCode
        // so on...
    })
);
data.SaveChanges();

Примечание: Это решение работает только в том случае, если у вас есть свойство навигации (внешний ключ) в классе Task (здесь называется "Инцидент" ). Если у вас этого нет, вы можете просто использовать одно из других опубликованных решений с помощью "AsQueryable()".

Ответ 9

Если вы используете инфраструктуру Entity, попробуйте удалить свойство из DbContext, который использует вашу сложную модель как Entity У меня была такая же проблема при отображении нескольких моделей в модель с именем Entity

public DbSet<Entity> Entities { get; set; }

Удаление записи из DbContext зафиксировало мою ошибку.

Ответ 10

если вы выполняете Linq to Entity, вы не можете использовать ClassType с new в select закрытии запроса only anonymous types are allowed (new without type)

взгляните на этот фрагмент моего проекта

//...
var dbQuery = context.Set<Letter>()
                .Include(letter => letter.LetterStatus)
                .Select(l => new {Title =l.Title,ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated,LetterStatus = new {ID = l.LetterStatusID.Value,NameInArabic = l.LetterStatus.NameInArabic,NameInEnglish = l.LetterStatus.NameInEnglish} })
                               ^^ without type__________________________________________________________________________________________________________^^ without type

вы добавили new keyword в ячейку Select, даже на complex properties, вы получите эту ошибку

так remove ключевое слово ClassTypes from new в Linq to Entity запросах,

потому что он преобразуется в оператор sql и выполняется на SqlServer

so , когда я могу использовать new with types в select закрытии?

вы можете использовать его, если вы имеете дело с LINQ to Object (in memory collection)

//opecations in tempList , LINQ to Entities; so we can not use class types in select only anonymous types are allowed
var tempList = dbQuery.Skip(10).Take(10).ToList();// this is list of <anonymous type> so we have to convert it so list of <letter>

//opecations in list , LINQ to Object; so we can use class types in select
list = tempList.Select(l => new Letter{ Title = l.Title, ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated, LetterStatus = new LetterStatus{ ID = l.LetterStatus.ID, NameInArabic = l.LetterStatus.NameInArabic, NameInEnglish = l.LetterStatus.NameInEnglish } }).ToList();
                                ^^^^^^ with type 

после того, как я выполнил ToList по запросу, он стал in memory collection, поэтому мы можем использовать new ClassTypes в select

Ответ 11

Вы можете решить эту проблему с помощью объектов передачи данных (DTO).

Это немного похоже на модели просмотра, в которые вы помещаете нужные вам свойства, и можете их сопоставить вручную в своем контроллере или с помощью сторонних решений, таких как AutoMapper.

С помощью DTO вы можете:

  • Сделать сериализуемыми данные (Json)
  • Избавьтесь от круговых ссылок
  • Уменьшить трафик сети, оставив свойства, которые вам не нужны (viewmodelwise)
  • Использование objectflattening

Я изучаю это в школе в этом году, и это очень полезный инструмент.

Ответ 12

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

Простой способ делает это:

IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();

Ответ 13

вы можете добавить AsEnumerable в свою коллекцию, как показано ниже:

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}