web-dev-qa-db-fra.com

Inclusion conditionnelle () dans Entity Framework

J'ai vu quelques réponses à des questions similaires, mais je n'arrive pas à trouver comment appliquer la réponse à mon problème.

var allposts = _context.Posts
            .Include(p => p.Comments)
            .Include(aa => aa.Attachments)
            .Include(a => a.PostAuthor)
            .Where(t => t.PostAuthor.Id == postAuthorId).ToList();

Les pièces jointes peuvent être téléchargées par l'auteur (type Auteur) ou le contributeur (type Contributeur). Ce que je veux faire, c'est obtenir les pièces jointes uniquement lorsque le propriétaire de la pièce jointe est de type Auteur.

Je sais que cela ne fonctionne pas et donne une erreur:

.Include(s=>aa.Attachments.Where(o=>o.Owner is Author))

J'ai lu sur Filtered Projection ici

EDIT - lien vers l'article:: http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx ,

mais je n'arrive pas à comprendre.

Je ne veux pas inclure le filtre dans la clause where finale car je veux TOUS les articles, mais je veux seulement récupérer les pièces jointes pour les articles qui appartiennent à l'auteur.

EDIT 2: - Post schéma demandé

public abstract class Post : IPostable
{

    [Key]
    public int Id { get; set; }

    [Required]
    public DateTime PublishDate { get; set; }

    [Required]
    public String Title { get; set; }

    [Required]
    public String Description { get; set; }

    public Person PostAuthor { get; set; }
    public virtual ICollection<Attachment> Attachments { get; set; }
    public List<Comment> Comments { get; set; }
}
15
grayson

À partir du lien que vous avez publié, je peux confirmer que cette astuce fonctionne, mais uniquement pour une relation un-plusieurs (ou plusieurs-un). Dans ce cas, votre Post-Attachment devrait être une relation un-plusieurs, donc c'est totalement applicable. Voici la requête que vous devriez avoir:

//this should be disabled temporarily
_context.Configuration.LazyLoadingEnabled = false;
var allposts = _context.Posts.Where(t => t.PostAuthor.Id == postAuthorId)
                       .Select(e => new {
                           e,//for later projection
                           e.Comments,//cache Comments
                           //cache filtered Attachments
                           Attachments = e.Attachments.Where(a => a.Owner is Author),
                           e.PostAuthor//cache PostAuthor
                        })
                       .AsEnumerable()
                       .Select(e => e.e).ToList();
11
Hopeless

Supprimez le mot clé virtual de votre propriété de navigation Attachments pour empêcher le chargement paresseux:

public ICollection<Attachment> Attachments { get; set; }

Première méthode: émettez deux requêtes distinctes: une pour les publications, une pour les pièces jointes, et laissez la correction des relations faire le reste:

List<Post> postsWithAuthoredAttachments = _context.Posts
    .Include(p => p.Comments) 
    .Include(p => p.PostAuthor)
    .Where(p => p.PostAuthor.Id == postAuthorId)
    .ToList();

List<Attachment> filteredAttachments = _context.Attachments
    .Where(a => a.Post.PostAuthor.Id == postAuthorId)
    .Where(a => a.Owner is Author)
    .ToList()

La correction des relations signifie que vous pouvez accéder à ces pièces jointes filtrées via la propriété de navigation d'un article

Deuxième méthode: une requête vers la base de données suivie d'une requête en mémoire:

var query = _context.Posts
    .Include(p => p.Comments) 
    .Include(p => p.PostAuthor)
    .Where(p => p.PostAuthor.Id == postAuthorId)
    .Select(p => new 
        {
            Post = p,
            AuthoredAttachments = p.Attachments
                Where(a => a.Owner is Author)
        }
    );

Je voudrais simplement utiliser le type anonyme ici

var postsWithAuthoredAttachments = query.ToList()

ou je créerais une classe ViewModel pour éviter le type anonyme:

List<MyDisplayTemplate> postsWithAuthoredAttachments = 
     //query as above but use new PostWithAuthoredAttachments in the Select

Ou, si vous voulez vraiment déballer les messages:

List<Post> postsWithAuthoredAttachments = query.//you could "inline" this variable
    .AsEnumerable() //force the database query to run as is - pulling data into memory
    .Select(p => p) //unwrap the Posts from the in-memory results
    .ToList()
4
Colin

Vous pouvez utiliser cette implémentation d'une méthode d'extension (par exemple.) Include2(). Après cela, vous pouvez appeler:

_context.Posts.Include2(post => post.Attachments.Where(a => a.OwnerId == 1))

Le code ci-dessus comprend uniquement les pièces jointes où Attachment.OwnerId == 1.

3
Jan Palas