L'entité ne peut pas être construite dans une requête LINQ to Entities

il y a un type d'entité appelé produit qui est généré par entity framework. 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 affiche l'erreur suivante:

"L'entité ou de type complexe de la Boutique.Le produit ne peut pas être construit dans un LINQ to Entities query "

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?

334
demandé sur Gilad Green 2011-03-16 16:12:58

13 réponses

Vous ne pouvez pas (et ne devrait pas être capable de) projet sur le mapping de l'entité. Vous pouvez, cependant, projeter sur un type annonyme 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 vous retournera une liste des DTO.

public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}
337
répondu Yakimych 2012-12-11 02:40:39

vous pouvez projeter dans le type anonyme, puis de lui au 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 });
}

Modifier : je vais être un peu plus précis, puisque cette question a obtenu beaucoup d'attention.

vous ne pouvez pas projeter directement dans le type de modèle (restriction EF), il n'y a donc pas d'autre solution. La seule façon est de projeter dans le type anonyme (1ère itération), puis de modéliser le type (2ème itération).

S'il vous plaît soyez également conscient que lorsque vous chargez partiellement des entités de cette manière, elles ne peuvent pas être mises à jour, donc elles doivent rester détachées, comme elles sont.

Je n'ai jamais complètement compris pourquoi ce n'est pas possible, et les réponses sur ce fil ne donnent pas de raisons fortes contre (principalement en parlant de données partiellement chargées). Il est exact que dans l'état partiellement chargé entité ne peut pas être mis à jour, mais alors, cette entité serait détachée, de sorte que les tentatives accidentelles de les sauver ne serait pas être possible.

considérez la méthode que j'ai utilisée ci-dessus: nous avons encore une entité de modèle partiellement chargée en conséquence. Cette entité est détachée.

(le souhaitez-pour-exister) code possible:

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

cela pourrait également donner lieu à une liste d'entités détachées, de sorte que nous n'aurions pas besoin de faire deux itérations. Un compilateur serait intelligent de voir que AsNoTracking () a été utilisé, ce qui résultera en des entités détachées, donc il pourrait nous permettre pour ce faire. Si, par contre, AsNoTracking() était omis, il pourrait lancer la même exception que celle qu'il lance maintenant, pour nous avertir que nous devons être suffisamment précis sur le résultat que nous voulons.

241
répondu Goran 2017-09-18 09:50:11

il y a une autre façon que j'ai trouvé des œuvres, vous devez construire une classe qui dérive de votre classe de produits et d'utiliser cela. 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};
}

je ne sais Pas si c'est "autorisé", mais il fonctionne.

68
répondu Tomasz Iniewicz 2012-04-28 14:52:04

Voici une façon de le faire sans déclarer la classe aditionnelle:

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;
}

cependant, ceci ne doit être utilisé que si vous voulez combiner plusieurs entités dans une seule entité. La fonctionnalité ci-dessus (simple product to product mapping) est fait comme ceci:

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;
}
33
répondu Bojan Hrnkas 2012-05-08 14:55:05

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;
}
21
répondu Soren 2017-01-14 05:28:15

vous pouvez utiliser ceci et cela devrait fonctionner -- > vous devez utiliser toList avant de faire la nouvelle liste en utilisant select:

db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList(); 
3
répondu Mohamed Adam 2016-09-20 08:39:32

ajouter 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};
}
2
répondu HamidReza 2017-05-06 12:40:39

en réponse à l'autre question qui a été marquée comme Double ( voir ici ) j'ai trouvé une solution rapide et facile 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();

Note: Cette solution ne fonctionne que si vous avez une propriété de navigation (clé étrangère) sur la classe de tâche (ici appelée "Incident"). Si vous ne l'avez pas, vous pouvez simplement utiliser l'une des autres solutions postées avec "AsQueryable()".

1
répondu JollyBrackets 2017-05-23 10:31:37

si vous utilisez Entity framework, puis essayer de supprimer la propriété de DbContext qui utilise votre modèle complexe comme entité J'ai eu le même problème lors de la mise en correspondance de plusieurs modèles dans un modèle de vue nommé Entity

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

Supprimer L'entrée de DbContext corrigé mon erreur.

0
répondu vikas suhag 2016-08-25 08:24:37

si vous exécutez Linq to Entity vous ne pouvez pas utiliser le ClassType avec new dans le 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 a ajouté le new keyword dans sélectionner la fermeture même sur le complex properties vous obtiendrez cette erreur

donc remove le ClassTypes from new mot-clé Linq to Entity des requêtes ,,

parce qu'il sera transformé en déclaration sql et exécuté sur SqlServer

donc 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 que j'ai exécuté ToList sur requête il est devenu in memory collection donc nous pouvons utiliser new ClassTypes dans sélectionner

0
répondu Basheer AL-MOMANI 2016-10-05 17:44:36

vous pouvez résoudre cela en utilisant des objets de transfert de données (DTO's).

ce sont un peu comme les modèles de vue où vous mettez dans les propriétés dont vous avez besoin et vous pouvez les mapper manuellement dans votre contrôleur ou en utilisant des solutions tierces comme AutoMapper.

avec DTO's vous pouvez:

  • Rendre les données serialisable (Json)
  • se débarrasser des références circulaires
  • réduire le trafic réseau de laissant propriétés que vous n'avez pas besoin (viewmodelwise)
  • Use objectflattening

j'ai appris cela à l'école cette année et c'est un outil très utile.

0
répondu Jelman 2016-12-01 09:44:58

Dans de nombreux cas, la transformation n'est pas nécessaire. Pensez à la raison pour laquelle vous voulez la liste de type fortement, et évaluez si vous voulez juste les données, par exemple, dans un service web ou pour l'afficher. Il n'a pas d'importance ce type. Vous avez juste besoin de savoir comment le lire et vérifier qui est identique aux propriétés définies dans le type anonyme que vous avez défini. C'est le scénario optimun, car quelque chose que vous n'avez pas besoin de tous les champs d'une entité, et c'est la raison pour laquelle anonymous type exister.

une manière simple est de le faire:

IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
0
répondu Sterling Diaz 2018-03-06 22:57:03

vous pouvez ajouter AsEnumerable à votre collection comme suit:

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}
-2
répondu HamidReza 2017-08-07 10:03:16