2016-04-19 11 views
5

śledzę tego przykładu, który dostałam od http://ef.readthedocs.org/en/latest/modeling/relationships.htmlwiele-do-wielu zapytań w Entity Framework 7

class MyContext : DbContext 
{ 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<Tag> Tags { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<PostTag>() 
      .HasKey(t => new { t.PostId, t.TagId }); 

     modelBuilder.Entity<PostTag>() 
      .HasOne(pt => pt.Post) 
      .WithMany(p => p.PostTags) 
      .HasForeignKey(pt => pt.PostId); 

     modelBuilder.Entity<PostTag>() 
      .HasOne(pt => pt.Tag) 
      .WithMany(t => t.PostTags) 
      .HasForeignKey(pt => pt.TagId); 
    } 
} 

public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; } 

    public List<PostTag> PostTags { get; set; } 
} 

public class Tag 
{ 
    public string TagId { get; set; } 

    public List<PostTag> PostTags { get; set; } 
} 

public class PostTag 
{ 
    public int PostId { get; set; } 
    public Post Post { get; set; } 

    public string TagId { get; set; } 
    public Tag Tag { get; set; } 
} 

Teraz moje pytanie brzmi, jak chciałbym skonstruować moje zapytanie, aby posty daną TagID? Coś jak:

public List<Post> GetPostsByTagId(int tagId) 
{ 
    //linq query here 
} 

Należy pamiętać, że jest to EF7.

Odpowiedz

9

Moja pierwsza rada to zmienić właściwości kolekcji do ICollection<T> zamiast List<T>. Możesz znaleźć naprawdę dobre wyjaśnienie w tym post.

Teraz wracając do prawdziwego problemu, to jak miałbym to zrobić zapytanie:

public List<Post> GetPostsByTadId(int tagId) 
{ 
    using(var context=new MyContext()) 
    { 
     return context.PostTags.Include(p=>p.Post) 
          .Where(pt=> pt.TagId == tagId) 
          .Select(pt=>pt.Post) 
          .ToList(); 
    } 
} 

Trzeba będzie chętny obciążenia Post właściwość nawigacji bo EF7 nie obsługuje leniwy załadunku, a także, jak @Igor zaleca się jego rozwiązania, należy uwzględnić PostTags jako DbSet w swoim kontekście:

public DbSet<PostTags> PostTags { get; set; } 

Objaśnienie:

Twoje zapytanie zaczyna się w tabeli PostTags, ponieważ znajduje się w tej tabeli, w której można znaleźć cały wpis powiązany z określonym znacznikiem. Zobacz Include jak połączenie wewnętrzne z tabelą Post. Jeśli zastosujesz łączenie między filtrowaniem PostTags i Posts przez TagId, otrzymasz kolumny, których potrzebujesz. W rozmowie Select mówisz, że potrzebujesz tylko kolumn z tabeli Post.

Po usunięciu połączenia Include nadal powinno działać. Z Include wyraźnie mówisz, że musisz zrobić sprzężenie, ale z Select, dostawca Linq EF jest wystarczająco inteligentny, aby zobaczyć, że musi on pośrednio wykonać sprzężenie, aby uzyskać kolumny Posts jako wynik.

+0

Nie jestem pewien jak to działa, ponieważ jestem nowy w EF, ale to zapytanie stworzyło najbardziej wydajną instrukcję SQL: "PostTags INNER JOIN Posts ...", gdzie inne odpowiedzi spowodowały absolutnie horrendalną zagnieżdżoną instrukcję SELECTs: 'SELECT ze Posts WHERE (WYBIERZ PRZYPADEK, KIEDY ISTNIEJĄ (WYBIERZ 1 Z POSTTAG ...'Czy możesz wyjaśnić, co się dzieje? –

+1

Pewnie, zaktualizowałem swoją odpowiedź – octavioccl

0

To naprawdę nie jest specyficzne dla EF7.

Można przedłużyć DbContext zawierać PostTags

class MyContext : DbContext 
{ 
    public DbSet<PostTags> PostTags { get; set; } 

Następnie zapytanie

db.Posts.Where(post => db.PostTags.Any(pt => pt.PostId == post.PostId && pt.TagId == tagId)) 
.Select(post => post); 
2
db.Posts.Where(post => post.PostTags.Any(pt => pt.TagId == tagId)); 
+0

To działa i jest prawdopodobnie najprostszym rozwiązaniem, ale patrząc na profilera SQL, stworzyło to dość skomplikowane i nieefektywne zapytanie z instrukcją CASE i zagnieżdżonymi zaznaczeniami. Nie wiem, dlaczego ... –

+0

@WheelBuilder Ponieważ EF Core nadal nie ma odpowiedniego tłumaczenia LINQ-SQL, a nie proste zapytania Forrward nie są tłumaczone na SQL i dlatego są wykonywane w pamięci po stronie klienta wykonującej N mniej prostszych zapytań SQL. Na przykład to samo dzieje się z GROUP BY, że na razie tłumaczenie na język SQL nie jest obsługiwane (wykonuje w pamięci tworzenie prostego zapytania SQL dla każdego wpisu w tabeli) i oczekuje się, że zostanie ono zaimplementowane w wersji EF Core v1.1, a nie w pełni. –