c# - net - La entidad no se puede construir en una consulta LINQ to Entities
entity framework curso (13)
Aquí hay una forma de hacer esto sin declarar una clase adicional:
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;
}
Sin embargo, esto solo se debe utilizar si desea combinar varias entidades en una sola entidad. La funcionalidad anterior (mapeo simple de producto a producto) se hace así:
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;
}
Hay un tipo de entidad llamada producto que es generado por el marco de la entidad. He escrito esta consulta
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
El siguiente código arroja el siguiente error:
"La entidad o tipo complejo Shop.Product no se puede construir en una consulta de LINQ to Entities"
var products = productRepository.GetProducts(1).Tolist();
Pero cuando uso select p
lugar de select new Product { Name = p.Name};
funciona correctamente
¿Cómo puedo preformar una sección de selección personalizada?
En muchos casos, la transformación no es necesaria. Piense por la razón por la que desea el tipo de lista, y evalúe si solo desea los datos, por ejemplo, en un servicio web o para mostrarlos. No importa el tipo. Solo necesita saber cómo leerlo y verificar que sea idéntico a las propiedades definidas en el tipo anónimo que definió. Ese es el escenario óptimo, porque algo no necesita todos los campos de una entidad, y esa es la razón por la que existe el tipo anónimo.
Una forma simple es haciendo esto:
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
En respuesta a la otra pregunta que estaba marcada como duplicada ( ver aquí ), encontré una solución rápida y fácil basada en la respuesta de Soren:
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();
Nota: esta solución solo funciona si tiene una propiedad de navegación (clave externa) en la clase de Tarea (aquí denominada ''Incidente''). Si no tiene eso, puede usar una de las otras soluciones publicadas con "AsQueryable ()".
Hay otra forma en la que encontré trabajos, tiene que crear una clase que se derive de su clase de Producto y usarla. Por ejemplo:
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};
}
No estoy seguro de si esto está "permitido", pero funciona.
No puede (y no debe poder) proyectar en una entidad asignada. Sin embargo, puede proyectar en un tipo anónimo o en un DTO :
public class ProductDTO
{
public string Name { get; set; }
// Other field you may need from the Product entity
}
Y su método devolverá una lista de DTO.
public List<ProductDTO> GetProducts(int categoryID)
{
return (from p in db.Products
where p.CategoryID == categoryID
select new ProductDTO { Name = p.Name }).ToList();
}
Otra forma sencilla :)
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''s not useful after "ToList()" :D
return productList;
}
Puede agregar AsEnumerable a su colección como la siguiente:
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
Puede proyectar en tipo anónimo, y luego desde él al tipo de modelo
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 });
}
Edit : Voy a ser un poco más específico ya que esta pregunta ha recibido mucha atención.
No puede proyectar directamente en el tipo de modelo (restricción EF), por lo que no hay forma de evitarlo. La única forma es proyectar en el tipo anónimo (primera iteración) y luego modelar el tipo (segunda iteración).
Tenga en cuenta que cuando carga entidades de esta manera, no pueden actualizarse, por lo que deben permanecer desconectadas, tal como están.
Nunca entendí completamente por qué esto no es posible, y las respuestas en este hilo no dan fuertes razones en contra de esto (en su mayoría hablan de datos parcialmente cargados). Es correcto que en la entidad de estado parcialmente cargada no se pueda actualizar, pero luego, esta entidad se desconectaría, por lo que no serían posibles los intentos accidentales de guardarlos.
Considere el método que usé anteriormente: todavía tenemos una entidad de modelo parcialmente cargada como resultado. Esta entidad está separada.
Considere este posible código (deseo de existir):
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new Product { Name = p.Name }).AsNoTracking().ToList();
Esto también podría resultar en una lista de entidades separadas, por lo que no tendríamos que hacer dos iteraciones. Un compilador sería inteligente para ver que se ha utilizado AsNoTracking (), lo que resultará en entidades separadas, por lo que podría permitirnos hacer esto. Sin embargo, si se omite AsNoTracking (), podría lanzar la misma excepción que está lanzando ahora, para advertirnos que debemos ser lo suficientemente específicos sobre el resultado que deseamos.
Puede resolver esto utilizando objetos de transferencia de datos (DTO).
Estos son un poco como modelos de vista en los que colocas las propiedades que necesitas y puedes mapearlas manualmente en tu controlador o usando soluciones de terceros como AutoMapper.
Con DTO''s puedes:
- Hacer que los datos sean serializables (Json)
- Deshazte de las referencias circulares.
- Reduzca el tráfico de red al dejar las propiedades que no necesita (a la vista)
- Usar objeto de aplanamiento
He estado aprendiendo esto en la escuela este año y es una herramienta muy útil.
Puedes usar esto y debería estar funcionando -> Debes usar toList
antes de hacer la nueva lista usando select:
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
Si está ejecutando Linq to Entity
no puede usar el ClassType
con new
en el cierre de select
only anonymous types are allowed (new without type)
mira este fragmento de mi proyecto
//...
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
Si agregó la new keyword
en el cierre de selección, incluso en las complex properties
, recibirá este error.
así que
remove
losClassTypes from new
palabra clave en las consultas deLinq to Entity
,
porque se transformará a la instrucción sql y se ejecutará en SqlServer
Entonces, ¿cuándo puedo usar new with types
en el cierre de select
?
puede usarlo si está tratando con 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
después de que ToList
en la consulta, se convirtió in memory collection
para que podamos usar new ClassTypes
en seleccionar
Si está utilizando Entity framework, intente eliminar la propiedad de DbContext que usa su modelo complejo como Entity. Tuvo el mismo problema al asignar múltiples modelos a un modelo de vista llamado Entity
public DbSet<Entity> Entities { get; set; }
Eliminar la entrada de DbContext corrigió mi error.
sólo agrega AsEnumerable ():
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}