LINQ to Entities ne reconnaît pas la méthode

116

J'obtiens l'erreur suivante en essayant de faire une requête linq:

LINQ to Entities ne reconnaît pas la méthode 'Boolean IsCharityMatching (System.String, System.String)', et cette méthode ne peut pas être traduite en une expression de magasin.

J'ai lu beaucoup de questions précédentes où les gens obtiennent la même erreur, et si je comprends bien, c'est parce que LINQ to Entities nécessite que toute l'expression de requête linq soit traduite en requête serveur, et par conséquent, vous ne pouvez pas appeler une méthode externe dedans. Je n'ai pas encore réussi à convertir mon scénario en quelque chose qui fonctionne, et mon cerveau commence à fondre, alors j'espérais que quelqu'un pourrait me diriger dans la bonne direction. Nous utilisons Entity Framework et le modèle de spécification (et je suis nouveau dans les deux).

Voici le code qui utilise la spécification:

ISpecification<Charity> specification = new CharitySearchSpecification(charityTitle, charityReference);

charities = charitiesRepository.Find(specification).OrderBy(p => p.RegisteredName).ToList();

Voici l'expression linq:

public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
    return p => p.IsCharityMatching(this.charityName, this.charityReference);
}

Voici la méthode IsCharityMatching:

public bool IsCharityMatching(string name, string referenceNumber)
{
    bool exists = true;

    if (!String.IsNullOrEmpty(name))
    {
        if (!this.registeredName.ToLower().Contains(name.ToLower()) &&
            !this.alias.ToLower().Contains(name.ToLower()) &&
           !this.charityId.ToLower().Contains(name.ToLower()))
        {
            exists = false;
        }
    }

    if (!String.IsNullOrEmpty(referenceNumber))
    {
        if (!this.charityReference.ToLower().Contains(referenceNumber.ToLower()))
        {
            exists = false;
        }
    }

    return exists;
}

Faites-moi savoir si vous avez besoin de plus d'informations.

Merci beaucoup,

Annelie

annélie
la source
vérifier cette réponse
Eranga
Je vais vérifier cela également, merci!
annelie
1
Ce serait bien de voir comment vous l'utilisez et Find()comment l'utiliser à l' IsSatisfied()intérieur.
Alisson

Réponses:

124

Comme vous l'avez compris, Entity Framework ne peut pas réellement exécuter votre code C # dans le cadre de sa requête. Il doit être capable de convertir la requête en une instruction SQL réelle. Pour que cela fonctionne, vous devrez restructurer votre expression de requête en une expression que Entity Framework peut gérer.

public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
    string name = this.charityName;
    string referenceNumber = this.referenceNumber;
    return p => 
        (string.IsNullOrEmpty(name) || 
            p.registeredName.ToLower().Contains(name.ToLower()) ||
            p.alias.ToLower().Contains(name.ToLower()) ||
            p.charityId.ToLower().Contains(name.ToLower())) &&
        (string.IsNullOrEmpty(referenceNumber) ||
            p.charityReference.ToLower().Contains(referenceNumber.ToLower()));
}
StriplingGuerrier
la source
1
en cas de doute, recherchez-le: stackoverflow.com/questions/2352764/…
Chris Hayes
2
Le retour d'un construit Expression<Func<T,type>>est une très belle approche à ce sujet.
Travis J
Comment utiliseriez-vous cela dans une expression LINQ? J'aimerais faire quelque chose comme ça en tant que clause Where réutilisable, mais j'ai du mal à l'appliquer.
Zorgarath
4
EDIT: tant pis, ce serait:context.Where(IsSatisfied())
Zorgarath
Partie clé: "Entity Framework ne peut pas réellement exécuter votre code C # dans le cadre de sa requête."
Alper
1

J'ai eu la même erreur dans ce code:

 var articulos_en_almacen = xx.IV00102.Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();

c'était exactement l'erreur:

System.NotSupportedException: 'LINQ to Entities ne reconnaît pas la méthode' Boolean Exists (System.Predicate`1 [conector_gp.Models.almacenes_por_sucursal]) 'méthode, et cette méthode ne peut pas être traduite en une expression de magasin.'

J'ai résolu de cette façon:

var articulos_en_almacen = xx.IV00102.ToList().Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();

J'ai ajouté un .ToList () avant ma table, cela dissocie l'entité et le code linq, et évite que ma prochaine expression linq soit traduite

REMARQUE: cette solution n'est pas optimale, car évitez le filtrage d'entités et charge simplement toutes les tables en mémoire

Ing. Gerardo Sánchez
la source
1
La plupart du temps, c'est la solution la plus simple, mais pour ne pas charger tous les objets, je fais généralement une sélection anonyme avant le .ToList () avec juste ce dont j'ai besoin ... xx.Select (x => new {x.Id, x.DateTimeUpdate }). ToList (). Select (x => new {x.Id, DateTimeUpdate = x.DateTimeUpdate.ToString ("dd / MM / yyyy")})
Diógenes
0

Si quelqu'un cherche une réponse VB.Net (comme je l'étais au départ), la voici:

Public Function IsSatisfied() As Expression(Of Func(Of Charity, String, String, Boolean))

Return Function(charity, name, referenceNumber) (String.IsNullOrWhiteSpace(name) Or
                                                         charity.registeredName.ToLower().Contains(name.ToLower()) Or
                                                         charity.alias.ToLower().Contains(name.ToLower()) Or
                                                         charity.charityId.ToLower().Contains(name.ToLower())) And
                                                    (String.IsNullOrEmpty(referenceNumber) Or
                                                     charity.charityReference.ToLower().Contains(referenceNumber.ToLower()))
End Function
Mik
la source
-1

J'ai eu un problème similaire au vôtre et cette documentation LINQ m'a aidé à trouver les bonnes fonctions de chaîne pour contourner les limitations.

Michael Fayad
la source