Wyszukiwanie po tagach - entity framework

0

Hej, mam 2 klasy POCO

    public partial class News
    {

        public int Id { get; set; }
        public string Title { get; set; }
        public string ShortArticle { get; set; }
        public string Article { get; set; }
        public string Author { get; set; }
        public System.DateTime Date { get; set; }
        public NewsState State { get; set; }
        public virtual List<Tag> Tags { get; set; }
    }
 public class Tag
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

Od paru dni próbuję zrobić wyszukiwanie wszystkich newsów o danym tagu, ale coś mi nie wychodzi :|
Nie mam pojęcia jak to zapisać w LINQ. Macie jakieś sugestie? Ta relacja jest w ogóle zaimplementowana?

0

A jak wyszukujesz?

0

No właśnie nie wyszukuję, bo nie wiem jak sformułować zapytanie LINQ. :D
Nie jestem nawet pewny, czy tą relację dobrze zdefiniowałem

public virtual List<Tag> Tags { get; set; } 

Udało mi się wymyślić coś takiego

var newsy = from p in db.News.ToList()
                            where p.Tags.Exists(x => x.Name.ToUpper() == tag.ToUpper())
                            select p; 

ale działa to bardzo wolno - do bazy wysyłanych jest ponad 500 zapytań (tyle, ile jest rekordów w tabeli z newsami).

0

.ToList() użyłem, bo inaczej dostaję wyjątek, że metoda Exists nie jest supportowana.
db to instancja klasy dziedziczącej po DbContext jak to bywa w entity frameworku.
Zawiera ona min.

public DbSet<News> News { get; set; }
public DbSet<Tag> Tags { get; set; }
1
var news = Db.News.Where(x => x.Tags.Contains(tag));
  • Przedefiniuj metode Equals w klasie Tag.
0

Zrobiłem tak jak napisałeś.
Obecnie moja akcja wygląda tak:

 public ActionResult Tag(string sTag)
            {
                Tag tag = new Tag { Name = sTag, Id = 1 };

                var newsy = db.News.Where(x => x.Tags.Contains(tag));

                //var newsy = from p in db.News
                //            where p.Tags.Contains(tag)
                //            select p;

                return View("~/Views/Szukaj/Index.cshtml", newsy);
            }
 

metoda Equals w Tag:

public override bool Equals(object obj)
        {
            Tag tag = obj as Tag;

            return tag.Name.ToUpper() == this.Name.ToUpper();
        }
 

Podczas widoku, a dokładniej na początku pętli foreach rzuca mi wyjątkiem System.NotSupportedException
Unable to create a constant value of type 'RapPierwszejKlasy.Models.Tag'. Only primitive types or enumeration types are supported in this context..
Występuje on w tej linijce:

     @foreach (var item in Model)

Na początku pliku z widokiem znajduje się oczywiście

 @model  IEnumerable<News>

.

Sam widok jest na 100% sprawny, bo wykorzystuję go jeszcze do wyszukiwania newsów po tytułach.

2

Ale co tu jest do kombinowania? Po co Contains i Equals, skoro wystarczy LINQ?

var newsWithTag = se.News.Where(n => n.Tags.Any(t => t.Name == "Ogórek")).ToList();

A tak na marginesie, to między News a Tags powinna być relacja wiele do wielu, bo obecnie każdy news ma swoje własne tagi, więc trzeba będzie ich treść powtarzać dla różnych artykułów.

0

#Encje raczej powinny byc porownywane po Id, a pozostale inne dziwne rzeczy powinny byc sprawdzane jak Id nie zostalo jeszcze wygenerowane (encja nie zostala spersistowana do bazy).
#Po uzyciu operatora as nalezy sprawdzac czy udalo sie rzutowac na typ docelowy -> http://msdn.microsoft.com/en-us/library/cscsdfbt.aspx
#Dodaj na koncu zapytania wywolanie metody AsEnumerable().

0
n0name_l napisał(a):
var news = Db.News.Where(x => x.Tags.Contains(tag));
  • Przedefiniuj metode Equals w klasie Tag.

Już wiem czemu mi to nie śmigało. Po prostu była konieczność utworzenia nowego obiektu Tag, a jako że wpadłem na pomysł aby nazwać go 'tag', to zmieniłem nazwę parametru akcji przez co nie pasowała tablica routingu >.<
Wielkie dzięki wszystkim za pomoc. Muszę jeszcze sobie ogarnąć te relacje many-to-many, bo rzeczywiście, obecnie nie jest to optymalnie rozwiązane.

0

Zmieniłem tą relację na wiele do wielu i mam jeszcze jedno pytanko.
Czy zapytanie

 
 var newsy = from p in db.News
                            where p.Tags.Any(x => x.Name.ToLower() == tag.ToLower())
                            select p;

dalej będzie dobrym rozwiązaniem? (działa)
Czy może lepiej pokombinować coś ala

 var newsy = from p in db.Tags
                            where p.Name.ToLower() == tag.ToLower()
                            select p.News;
 

Chodzi mi tutaj o wybór bardziej wydajnego oraz semantycznie poprawnego podejścia.

1

Tak, to nadal będzie dobre rozwiązanie, o ile EF wygeneruje sensowny SQL, i nie spowoduje jakiegoś N+1. Najlepiej sprawdzić SQL profilerem.
Okropna jest ta składnia LINQ, brrr.

1 użytkowników online, w tym zalogowanych: 0, gości: 1