Сущность не может быть построена в запросе 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};
}