Seuls les constructeurs et 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 EF enity, MatchHistory est, donc ne peut pas comprendre pourquoi il se plaint.

Tous les conseils appréciés.

27
demandé sur Yuck 2013-06-28 18:54:10

4 réponses

La raison pour laquelle il se plaint est parce qu'il ne sait pas comment traduire votre Select() en une expression SQL. Si vous devez effectuer une transformation de données vers 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 prudent avec cela, cependant, et assurez-vous que vous limitez la taille de l'ensemble de données retourné par ToList() autant que possible de sorte que vous n'essayez pas de charger une table entière en mémoire.

48
répondu Yuck 2013-06-28 15:12:02

Le Message est clair: linq to entities 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 sur celui-ci)

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

Solution2

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

public FundedCount() {}

Et utiliser

.Select(x => new FundedCount{
                        <Property1> = x.Key.BuyerId,
                        <Property2> = x.Count() / 30 * daysInMonth
                         })
                    .ToList();
11
répondu Raphaël Althaus 2013-06-28 15:28:21

Il se plaint car il ne peut pas Convertir les références en fundedcount En instructions SQL.

Tous les fournisseurs LINQ convertissent les instructions et expressions LINQ en opérations que leur cible peut comprendre. LINQ en SQL et LINQ en EF convertiront 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 faire la conversion, dépend du fournisseur. Certains fournisseurs renverront des résultats intermédiaires et convertir 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 lorsque seulement 1 ou 2 serait réellement nécessaire.

Dans votre cas, vous devez modifier votre select pour renvoyer un type anonyme avec les données souhaitées, appeler ToList (), puis 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();

Le premier ToList() forcera la génération de L'instruction SQL et exécutera la requête qui ne retournera que 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
répondu Panagiotis Kanavos 2013-06-28 15:15:07

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

J'avais un GroupBy() dans ma requête "Linq to EntityFramework" qui utilisait une structure comme clé dans GroupBy. Qui n'a pas fonctionné. 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() {...}
2
répondu David Votrubec 2014-03-20 09:57:42