web-dev-qa-db-fra.com

Seuls les constructeurs et les initialiseurs sans paramètre sont pris en charge dans le message LINQ to Entities

J'ai une méthode qui renvoie les données d'un modèle EF.

Je reçois le message ci-dessus, mais je ne peux pas savoir comment contourner le problème.

    public static IEnumerable<FundedCount> GetFundedCount()
    {
        var today = DateTime.Now;
        var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);

        var day1 = DateTime.Now.AddDays(-1);
        var day31 = DateTime.Now.AddDays(-31);

        using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
        {
            var r = new Repository<MatchHistory>(uow.Context);

            return r.Find()
                .Where(x =>
                    x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
                    x.ResultTypeId == (int)MatchResultType.Accepted)
                .GroupBy(x => new { x.BuyerId, x.AppliedOn })
                .Select(x => new FundedCount(
                    x.Key.BuyerId,
                    x.Count() / 30 * daysInMonth))
                .ToList();
        }
    }

FundedCount n'est pas une entité EF, MatchHistory l'est, donc vous ne pouvez pas comprendre pourquoi il se plaint.

Tous les conseils sont appréciés.

29
dotnetnoob

La raison pour laquelle il se plaint est qu'il ne sait pas comment traduire votre Select() en une expression SQL. Si vous devez effectuer une transformation de données en un POCO qui n'est pas une entité, vous devez d'abord obtenir les données pertinentes d'EF puis les transformer en POCO.

Dans votre cas, cela devrait être aussi simple que d'appeler ToList() plus tôt:

return r.Find()
        .Where(x => x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
                    x.ResultTypeId == (int)MatchResultType.Accepted)
        .GroupBy(x => new { x.BuyerId, x.AppliedOn })
        .ToList() // this causes the query to execute
        .Select(x => new FundedCount(x.Key.BuyerId, x.Count() / 30 * daysInMonth));

Soyez toutefois prudent et assurez-vous de limiter autant que possible la taille de l'ensemble de données renvoyé par ToList() afin de ne pas essayer de charger une table entière en mémoire.

51
Yuck

Le message est clair: linq to entity ne prend pas en charge les objets sans ctor sans paramètre.

Donc

Solution1

énumérer avant (ou utiliser un type anonyme intermédiaire et énumérer celui-là)

.ToList()
.Select(x => new FundedCount(
                    x.Key.BuyerId,
                    x.Count() / 30 * daysInMonth))
                .ToList();

Solution2

ajouter un ctor sans paramètre à votre classe FundedCount (si c'est possible)

public FundedCount() {}

et utilise

.Select(x => new FundedCount{
                        <Property1> = x.Key.BuyerId,
                        <Property2> = x.Count() / 30 * daysInMonth
                         })
                    .ToList();
12
Raphaël Althaus

Il se plaint car il ne peut pas convertir les références à FundedCount en instructions SQL.

Tous les fournisseurs LINQ convertissent les instructions et expressions LINQ en opérations que leur cible peut comprendre. LINQ to SQL et LINQ to EF convertira LINQ en SQL, PLINQ le convertira en tâches et opérations parallèles, LINQ en Sharepoint le convertira en CAML, etc.

Ce qui se passe s'ils ne peuvent pas effectuer la conversion, dépend du fournisseur. Certains fournisseurs renvoient des résultats intermédiaires et convertissent le reste de la requête en une requête LINQ to Objects. D'autres échoueront simplement avec un message d'erreur.

Échouer avec un message est en fait une meilleure option lorsque vous parlez à une base de données. Sinon, le serveur devrait renvoyer toutes les colonnes au client alors que seulement 1 ou 2 seraient réellement nécessaires.

Dans votre cas, vous devez modifier votre sélection pour renvoyer un type anonyme avec les données souhaitées, appeler ToList () et ALORS créer les objets FundedCount, par exemple:

.Select( x=> new {Id=x.Key.BuyerId,Count=x.Count()/30 * daysInMonth)
.ToList()
.Select(y => new FundedCount(y.Id,y.Count))
.ToList();

La première ToList () forcera la génération de l'instruction SQL et exécutera la requête qui renverra uniquement les données dont vous avez besoin. Le reste de la requête est en fait Linq to Objects et obtiendra les données et créera les objets finaux

3
Panagiotis Kanavos

J'ai eu la même exception dans GroupBy. J'ai trouvé que l'exception "Seuls les constructeurs et initialiseurs sans paramètre sont pris en charge dans LINQ to Entities" n'est pas une description exacte à 100%.

J'avais un GroupBy () dans ma "requête Linq to EntityFramework" qui utilisait une structure comme clé dans GroupBy. Ça n'a pas marché. Quand j'ai changé cette structure en classe normale, tout a bien fonctionné.

Exemple de code

var affectedRegistrationsGrouped = await db.Registrations
  .Include(r => r.Person)
  .Where(r =>
      //whatever
  )
  .GroupBy(r => new GroupByKey
  {
      EventId = r.EventId, 
      SportId = r.SportId.Value
  })
 .ToListAsync();

...
...
// this does not work
private struct GroupByKey() {...}

// this works fine
private class GroupByKey() {...}
3
David Votrubec