web-dev-qa-db-fra.com

Erreur 1025 du fournisseur de données .NET Framework interne

IQueryable<Organization> query = context.Organizations;

Func<Reservation, bool> predicate = r => !r.IsDeleted;

query.Select(o => new { 
    Reservations = o.Reservations.Where(predicate)
}).ToList();

cette requête lève l'exception "Erreur interne du fournisseur de données .NET Framework 1025", mais pas la requête ci-dessous.

query.Select(o => new { 
    Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();

J'ai besoin d'utiliser le premier parce que j'ai besoin de vérifier quelques instructions if pour construire le bon prédicat. Je sais que je ne peux pas utiliser d'instructions if dans cette circonstance, c'est pourquoi je passe un délégué comme paramètre.

Comment puis-je faire fonctionner la première requête?

55
Freshblood

Bien que les réponses ci-dessus soient vraies, notez que lorsque vous essayez de l'utiliser après une instruction select, vous devez appeler AsQueryable() explicitement, sinon le compilateur supposera que nous essayons d'utiliser les méthodes IEnumerable, qui attendent un Func et non Expression<Func>.

C'était probablement le problème de l'affiche originale, sinon le compilateur se plaindra la plupart du temps qu'il recherche Expression<Func> et non Func.

Démo: Les éléments suivants échoueront:

MyContext.MySet.Where(m => 
      m.SubCollection.Select(s => s.SubItem).Any(expr))
         .Load()

Bien que les éléments suivants fonctionnent:

MyContext.MySet.Where(m => 
      m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
         .Load()
35
yoel halb

Après la création de la prime (rats!), J'ai trouvé cette réponse , qui a résolu mon problème. (Mon problème impliquait un appel à .Any(), ce qui est un peu plus compliqué que cette question ...)

En bref, voici votre réponse:

IQueryable<Organization> query = context.Organizations;

Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;

query.Select(o => new { Reservations = o.Reservations.Where(expr) })
  .ToList();

Lisez la réponse référencée pour savoir pourquoi vous avez besoin de la variable locale expr, et vous ne pouvez pas référencer directement une autre méthode de type de retour Expression<Func<Reservation, bool>>.

25
Shaul Behr

Merci de m'avoir cinglé. Je suppose que j'étais sur la bonne voie après tout.

Quoi qu'il en soit, pour réitérer, LINQ to Entities (merci à Jon Skeet de m'avoir corrigé quand je me suis mêlé à ma propre réflexion dans les commentaires) fonctionne sur Expression Trees ; il permet une projection pour traduire l'expression lambda en SQL par le QueryProvider .

Ordinaire Func<> fonctionne bien pour LINQ to Objects.

Donc, dans ce cas, lorsque vous utilisez Entity Framework, tout prédicat passé au IQueryable du FE doit être le Expression<Func<>>.

19
Patryk Ćwiek

Je viens de rencontrer ce problème dans un scénario différent.

J'ai une classe statique pleine de prédicats Expression que je peux ensuite combiner ou passer à une requête EF. L'un d'eux était:

    public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
        IEnumerable<EventEnums.AttendeeStatus> statuses)
    {
        return ce => ce.Event.AttendeeStatuses
            .Where(a => a.ClientId == ce.Client.Id)
            .Select(a => a.Status.Value)
            .Any(statuses.Contains);
    }

Cela lançait l'erreur 1025 en raison de l'appel de groupe de méthode Contains. Le framework d'entité attendait un Expression et a trouvé un groupe de méthodes, ce qui a entraîné l'erreur. La conversion du code pour utiliser un lambda (qui peut être implicitement converti en une expression) a corrigé l'erreur

    public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
        IEnumerable<EventEnums.AttendeeStatus> statuses)
    {
        return ce => ce.Event.AttendeeStatuses
            .Where(a => a.ClientId == ce.Client.Id)
            .Select(a => a.Status.Value)
            .Any(x => statuses.Contains(x));
    }

En plus: j'ai ensuite simplifié l'expression en ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));

5
AlexFoxGill

Eu un problème similaire. Bibliothèque de ViewModels qui ressemble à ceci:

public class TagViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
    {
        Id = t.Id,
        Name = t.Name,
    };

Cela marche:

var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
    .ToArrayAsync();

Mais cela ne compilera pas:

var post = await db.Posts.Take(10)
    .Select(p => new {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
    })
    .ToArrayAsync();

Parce que le second .Select Est un gâchis - le premier est en fait appelé hors d'une ICollection, qui n'est pas IQueryable, donc il consomme cette première expression sous la forme d'un Func simple, pas Expression<Func.... Cela renvoie IEnumerable<..., Comme indiqué sur cette page. Alors .AsQueryable() à la rescousse:

var post = await db.Posts.Take(10)
    .Select(p => new {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
            .Select(TagViewModel.Select)
    })
    .ToArrayAsync();

Mais cela crée un nouveau problème plus étrange: soit je reçois le cadre interne ... Erreur 1025, soit je reçois la variable de publication avec une propriété .Post Entièrement chargée, mais la propriété .Tags A un EF objet proxy qui semble être utilisé pour le chargement différé.

La solution consiste à contrôler le type de retour des balises, en mettant fin à l'utilisation de la classe Anonymous:

public class PostViewModel
{
    public Post Post { get; set; }
    public IEnumerable<TagViewModel> Tags { get; set; }

Maintenant, sélectionnez-le et tout fonctionne:

var post = await db.Posts.Take(10)
    .Select(p => new PostViewModel {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
            .Select(TagViewModel.Select)
    })
    .ToArrayAsync();
0
Chris Moschini