EF - שאילתא על טבלת המבצעת קשר גומלין של רבים לרבים
-
דוגמא קלסית של מסד נתונים של בלוגים, לכל בלוג יכולים להיות כמה תגים, ולכל תג יכולים להיות שייכים כמה בלוגים, אז בקוד יוצרים רק מחלקה 'בלוג' עם מאפיין 'תגים' ומחלקה 'תג' עם מאפיין 'בלוגים' ו EF יוצר מאחורי הקלעים טבלה 'בלוג_תג' שבכל רשומה שלה יש מזהה בלוג ומזהה תג לאמר שבלוג זה מכיל תג זה ותג זה מכיל בלוג זה.
עד כאן דברים ידועים, כעת השאלה איך אני יכול לעשות שאילתא על הטבלה הנסתרת הזאת?
המטרה שלי היא לשלוף את כל הבלוגים שיש להם תג א' ותג ב' ואין להם תג ג' ותג ד'?
אז אני יכול לגשת למאפיין 'תגים' שבמחלקת בלוג ולבדוק איזה תגים יש שם, אבל אז בא ה EF ורואה שאני ניגש למאפיין 'תגים' ומריץ שאילתא כדי למלאת לי את המערך של התגים, והוא עושה את זה כפול כל הבלוגים שהם כמה עשרות אלפים, וכך רצות עשרות אלפי שאילתות מיותרות לכאורא.
לכן אני מחפש לעשות את זה כשאילתא יותר ישירה למסד נתונים בלי לגשת למאפיין 'תגים' אלא משהוא על הטבלה הנסתרת הזו.
יש למישהו רעיון?אם הייתי רוצה לעשות את זה ישירות ב SQL הייתי כותב כך:
SELECT * FROM Blogs WHERE Id IN (SELECT BlogId FROM Blogs_Tags WHERE TagId = 1 INTERSECT SELECT BlogId FROM Blogs_Tags WHERE TagId = 3 EXCEPT SELECT * FROM (SELECT BlogId FROM Blogs_Tags WHERE TagId = 2 INTERSECT SELECT BlogId FROM Blogs_Tags WHERE TagId = 4))
אבל כיון שאני עובד עם EF אני מחפש לעשות את זה בLINQ כמו שנוהגים ב EF, האם יש דרך?
פורסם במקור בפורום CODE613 ב30/05/2017 15:10 (+03:00)
-
אתה טועה, הEF מבין אותך מצויין. כל עוד אינך מייצא את תוצאות השאילתה עד לביטויה המלא, הEF מתרגם מעולה שאילתות סבוכות מאוד.
יש כמה אופנים לכתוב את מה שאתה רוצה, למשל:var blogsWith = db.Blogs.Where(x => x.Tags.Any(y => y.id == 1 || y.id == 3)); var blogWithout = blogsWith.Where(x => !x.Tags.Any(y => y.id == 2 || y.id == 4)); Console.WriteLine(blogWithout);
אתה גם יכול לעשות את הטבלה של תג-בלוג למפורשת וככה לתשאל אותה ישירות.
פורסם במקור בפורום CODE613 ב01/06/2017 00:12 (+03:00)
-
@רחמים
איך עושים את זה?var blogs = db.Blogs.Where(x => x.Tags.Any(y => y.id == 1 && y.id == 3 && y.id != 2 && y.id != 4));
ID לעולם לא יכול להיות שווה גם 1 וגם 3, אני צריך שיביא לי את כל הבלוגים, שבין התגים שלהם יש תג אחד עם מזהה 1 ותג אחרעם מזהה 3
פורסם במקור בפורום CODE613 ב01/06/2017 09:26 (+03:00)
-
תודה,
איך באמת EF עובד, על פי איזה כללים? אני רוצה לדעת מראש אם ביטוי LINQ מסויים יהפך לשאילתא כמו שאני רוצה.
אין פה כללים, אם השאילתה מבטאת את מה שביקשת, אתה יכול לסמוך על EF בעיניים עצומות.
אתה רק צריך א. לא להשתמש בביטויים שלא נתמכים לתרגום לSQL, כמו פונקציות מקומיות וכדומה. זה מעיף שגיאה.
ב. לא לייצא את השאילתה לפני סוף התשאול, למשל בדוגמה הקודמת עם תעשה ToList לפני סיום ההתניה זה יהיה פלטור של Linq To Object ולא SQL בצד השרת.פורסם במקור בפורום CODE613 ב01/06/2017 10:55 (+03:00)
-
@דוד ל.ט.
var blogsWith = db.Blogs.Where(x => x.Tags.Any(y => y.id == 1) && x.Tags.Any(y => || y.id == 3));
מצויין [רק צריך למחוק את ||]
כעת אני צריך להטמיע את זה בטופס הסינון שהמשתמשים יוכלו לחפש בלוגים על פי תגים שיש ו/או שאין
השאלה איך אני בונה ביטוי LINQ כזה בצורה דינמית שכמות התגים משתנה וגם כמובן מזהי התגים צריך להגדיר בזמן ריצה.פורסם במקור בפורום CODE613 ב01/06/2017 11:00 (+03:00)
-
אם כוונתך איך לנסח את השאילתה מול ID של תגיות בצורה דינמית, אתה צריך לאחזר את רשימת התגיות הרלוונטיות ואח"כ לעבוד עם Contains בהתנייה. הנה למשל מתודה גמישה לזמן ריצה:
IQueryable<Blog> ByTags(IQueryable<Blog> blogs, IQueryable<Tag> have, IQueryable<Tag> prohibited) { var with = blogs.Where(x => have.All(y => x.Tags.Contains(y))); return with.Where(x => !x.Tags.Any(y => prohibited.Contains(y))); }
קוד מריץ עם הקריטריונים לעיל:
var tagsTrue = db.Tags.Where(x => x.id == 1 || x.id == 3); var tagsFalse = db.Tags.Where(x => x.id == 2 || x.id == 4); var blogs = ByTags(db.Blogs, tagsTrue, tagsFalse); Console.WriteLine(blogs);
פורסם במקור בפורום CODE613 ב01/06/2017 13:05 (+03:00)