Il existe un type d'entité appelé produit qui est généré par le cadre de l'entité. J'ai écrit cette requête
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
Le code ci-dessous lève l'erreur suivante:
"L'entité ou le type de complexe Shop.Product ne peut pas être construit dans une requête LINQ to Entities"
var products = productRepository.GetProducts(1).Tolist();
Mais quand j'utilise select p
au lieu de select new Product { Name = p.Name};
, cela fonctionne correctement.
Comment puis-je préformer une section de sélection personnalisée?
Vous ne pouvez pas (et ne devriez pas pouvoir) projeter sur une entité mappée. Vous pouvez toutefois projeter sur un type anonyme ou sur un DTO :
public class ProductDTO
{
public string Name { get; set; }
// Other field you may need from the Product entity
}
Et votre méthode retournera une liste 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();
}
Vous pouvez projeter en type anonyme, puis en type de modèle
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: Je vais être un peu plus précis car cette question a attiré beaucoup d'attention.
Vous ne pouvez pas projeter directement dans le type de modèle (restriction EF), il n'y a donc aucune solution. Le seul moyen consiste à projeter dans un type anonyme (1ère itération), puis à modéliser un type (2ème itération).
Sachez également que lorsque vous chargez partiellement des entités de cette manière, elles ne peuvent pas être mises à jour. Elles doivent donc rester détachées telles quelles.
Je n'ai jamais complètement compris pourquoi ce n'est pas possible, et les réponses sur ce fil ne donnent pas de raison forte de le contrer (principalement en ce qui concerne les données partiellement chargées). Il est correct que dans une entité d'état partiellement chargée ne puisse pas être mise à jour, cette entité étant alors détachée, des tentatives accidentelles de sauvegarde ne seraient pas possibles.
Considérez la méthode que j'ai utilisée ci-dessus: nous avons toujours une entité de modèle partiellement chargée en conséquence. Cette entité est détachée.
Considérez ce code possible (souhait-exister):
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new Product { Name = p.Name }).AsNoTracking().ToList();
Cela pourrait également donner une liste d'entités détachées, nous n'aurions donc pas besoin de faire deux itérations. Un compilateur serait bien avisé de voir que AsNoTracking () a été utilisé, ce qui entraînera des entités détachées, ce qui pourrait nous permettre de le faire. Si, toutefois, AsNoTracking () était omis, il pourrait émettre la même exception qu’il le fait actuellement, pour nous avertir que nous devons être suffisamment précis sur le résultat souhaité.
J'ai trouvé une autre méthode qui fonctionne: vous devez créer une classe dérivée de votre classe Product et l'utiliser. Par exemple:
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};
}
Vous ne savez pas si cela est "autorisé", mais cela fonctionne.
Voici un moyen de le faire sans déclarer de classe supplémentaire:
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;
}
Toutefois, cela ne doit être utilisé que si vous souhaitez combiner plusieurs entités en une seule entité. La fonctionnalité ci-dessus (mappage simple de produit à produit) est réalisée comme suit:
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;
}
Un autre moyen simple :)
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;
}
Vous pouvez utiliser ceci et il devrait fonctionner -> Vous devez utiliser toList
avant de créer la nouvelle liste en utilisant select:
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
Vous pouvez résoudre ce problème en utilisant des objets de transfert de données (DTO).
Ce sont un peu les modèles de vue dans lesquels vous insérez les propriétés dont vous avez besoin et que vous pouvez mapper manuellement dans votre contrôleur ou en utilisant des solutions tierces telles qu'AutoMapper.
Avec DTO, vous pouvez:
J'ai appris cela à l'école cette année et c'est un outil très utile.
En réponse à l’autre question qui était marquée comme une copie ( voir ici ), j’ai trouvé une solution simple et rapide basée sur la réponse 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();
Remarque: cette solution ne fonctionne que si vous avez une propriété de navigation (clé étrangère) sur la classe Task (appelée ici "Incident"). Si vous n'en avez pas, vous pouvez simplement utiliser l'une des autres solutions publiées avec "AsQueryable ()".
ajoutez seulement AsEnumerable ():
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
si vous exécutez Linq to Entity
, vous ne pouvez pas utiliser la ClassType
avec new
dans la select
fermeture de la requête only anonymous types are allowed (new without type)
regardez cet extrait de mon projet
//...
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
de vous avez ajouté le new keyword
dans Sélectionner fermeture même sur le complex properties
vous aurez cette erreur
so
remove
leClassTypes from new
mot-clé surLinq to Entity
requêtes,
car il sera transformé en instruction SQL et exécuté sur SqlServer
alors quand puis-je utiliser new with types
sur select
fermeture?
vous pouvez l'utiliser si vous avez affaire à 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
après avoir exécuté ToList
sur une requête, il est devenu in memory collection
afin que nous puissions utiliser new ClassTypes
dans la sélection
Si vous utilisez la structure Entity, essayez de supprimer la propriété de DbContext qui utilise votre modèle complexe en tant qu'entité J'ai rencontré le même problème lors du mappage de plusieurs modèles dans un modèle de vue nommé Entity.
public DbSet<Entity> Entities { get; set; }
Supprimer l’entrée de DbContext a corrigé mon erreur.
Dans de nombreux cas, la transformation n'est pas nécessaire. Pensez à la raison pour laquelle vous souhaitez que le type soit fortement typé, et déterminez si vous souhaitez uniquement les données, par exemple dans un service Web ou pour les afficher. Peu importe le type. Vous avez juste besoin de savoir comment le lire et de vérifier qu'il est identique aux propriétés définies dans le type anonyme que vous avez défini. C'est le scénario optimun, car vous n'avez pas besoin de tous les champs d'une entité et c'est la raison pour laquelle le type anonyme existe.
Un moyen simple est de faire ceci:
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();