IQueryable as tamplate

Sea Bass

New member
IQueryable as tamplate

יש לי שאלה שקצת קשה לי להסביר אבל בכל זאת...
אני מעוניין לייצר IQueryable ולהשתמש בזה כטמפלט ולבצע לו Excecute ב EF..
נניח יש לי Entity User.
אז לעשות משהו בסגנון:
IQueryable temlate = from user.where(x=> x.id==1).select(x=> x.age).First
ואת ה template הזה להעביר ל EF.
נניח שה IQueryable הזה נמצא באיזה Static Dictionary...
אני מסביר טיפה דפוק אבל אני מקווה שיש מישהו שמבין.
אנלוגיה מ SQL בצורה דינאמית.
הייתי רושם HARD CODED:
"public const string UserTemplate = "select top 1 age from user where user.id =1
והטמפלט הזה אני מריץ מתי שאני רוצה..
 
אולי כוונתך לזה

public class MyBussinessObject{
public int Id { get; set; }
public DateTime Date { get; set; } public string Name { get; set; } public decimal Sales { get; set; }}public class MyContext{ public IQueryable<MyBussinessObject> MyBussinessObjects { get; set; }}public static class Program{ private static Expression<Func<MyBussinessObject, bool>> predicate1 = item => FilterById(item); private static Expression<Func<MyBussinessObject, bool>> predicate2 = item => item.Name == "XYZ"; public static void Main() { MyContext context = new MyContext(); var vv = context.MyBussinessObjects.Where(predicate1).Where(predicate2).ToList(); } static bool FilterById(MyBussinessObject obj) { return obj.Id > 10; }}
 

Sea Bass

New member
לא בדיוק אבל..

כבר הסתדרתי ועם כבר אז..
לא בדיוק הצלחתי להבין את השימוש של Expression< ולמה ואיפה משתמשים בזה (ולמה השתמשת בזה פה).
אף פעם לא ראיתי את השימוש הזה בקוד.
 
ייתכן

שהמאמר הזה לא ייתן לו את המענה, כי הוא למשתמשים די מתקדמים.
[הסיבה שלא עניתי על שאלתו, כי אינני יודע מה רמת הידע שלו]

מה שנחוץ לדעת במקרה שלנו הוא פשוט בתכלית: Expression הוא מעטפת לפונקציה, שהמנוע של הQueryable מפלטר באמצעותו.

הנה הגדרת המתודה Where של הממשק IEnumerable:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

לעומת זאת המתודה Where של הממשק IQueryable
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

דומה, אבל שונה...

איך יוצרים את הExpression? באמצעות Lambda.
ולכן, בכל פעם שאתה מפלטר את האובייקטים של EntityFramework, אתה למעשה יוצר Expression.

שים לב, שלא רק Expression יוצרים באמצעות Lambda, אלא גם Func.
הקומפיילר מספיק חכם כדי לדעת מתי ליצור כל דבר.

למשל, בקוד הבא יש שלשה משפטי למדא, הראשון יצר Func כי הוא נשלח למתודה Where של IEnumerable.
השני, יוצר Expression, כי הוא נשלח למתודה Where של IQueryable.
השלישי, שווה ערך לשני.

List<MyObject> list = new List<MyObject>();var listFilter = list.Where(x => x.Name = "XYZ");
var context = new MyContext();
var entityFilter = context.MyObjects.Where(x => x.Name = "XYZ");
Expression<Func<MyObject, bool>> exp = x => x.Name = "XYZ";entityFilter = context.MyObjects.Where(exp);
 
עוד משפט הסבר:

הסיבה שהמתודה Where של IQueyable שונה, היא פשוטה:

המתודה Where של IEnumerable מקבלת פונקציה בוליאנית כארגומנט, ועושה דבר אחד ויחיד: לולאת foreach על האוסף, ובכל סיבוב של הלולאה היא מריצה את הפונקציה על הפריט הנוכחי כדי לבדוק האם הוא עומד בקריטריון.

לעומת זאת, IQueryable עושה דברים אחרים לגמרי. לדוגמא: המתודה Where של אוסף EntityFramework, יוצר משפט SQL. לכן, לא מספיק לא לקבל פונקציה, והוא צריך שתעטוף את הפונקציה לביטוי, שהוא יודע להמיר אותו לSQL.
 
This phrase implies LINQ-to-object has no optimizations

Which is not the case. For example, AFAIK, chained Where(...) clauses will compile to a single loop with multiple conditions.
In fact, it's not even guaranteed that foreach will be used.
עם זאת כהסבר "מסדר ראשון" זה הסבר מצויין
 

Royi Namir

New member
למיטב ידיעתי

foreach will be used.
But remember that foreach under the hood is nothing but :

E enumerator = (collection).GetEnumerator();try {
ElementType element; //pre C# 5 while (enumerator.MoveNext()) { ElementType element; //post C# 5 element = (ElementType)enumerator.Current; statement; }}finally { IDisposable disposable = enumerator as System.IDisposable; if (disposable != null) disposable.Dispose();}



לגבי החלק הראשון של המשורשרים - זה בהחלט נשמע מעניין ואני מתכוון לחקור את זה.
תודה.
 
בהנתן שהטיפוס הקונקרטי של IEnumerable מממש random access

הביטוי אמור להתקפל ללולאת for. כך שמעתי - אני ראשון להודות שלא בדקתי את זה
 
מעניין, היתה לי קונספציה שגויה בנושא.

די מפתיע האמת - ביטוי כמו
Enumerable.Range(1,7) .Where (num => num < 5)
.Where (num => num < 5);
מייצר MSIL עם פעמיים קוד (זהה) של הפרדיקטור, שגם נקרא פעמיים.
אם מוסיפים as IQueryable מתקבל קוד IL הרבה יותר מסובך... אני ממש לא בטוח שאני קורא אותו נכון, אבל נראה שבאמת יש פרדיקטור יחיד.
 
code review

עשיתי רפלקשיין [עם הרפלקטור המצוין של טלריק :)]
ומסתבר שהמתודה Where יוצרת מחזירה אובייקט אינטרנלי בשם WhereSelectEnumerableIterator, המממש את הממשק IEnumerator. וכך הוא מממש את המתודה MoveNext:

public override bool MoveNext(){
// this.enumerator is the original enumerator of the source
while (this.enumerator.MoveNext()) { TSource current = this.enumerator.Current; if (!this.predicate(current)) { continue; } this.current = current; return true; } this.Dispose(); break; }return false;}
במילים אחרות, הפונקציה Where איננה מחליטה לעשות ForEach אלא כאשר אתה עושה MoveNExt [באמצעות foreach או בכל דרך אחרת] הפונקציה Where מריצה אותך קדימה אם אתה לא עומד בקריטריון.
זו גם הסיבה שרועי צודק. אין אופטימיזציה. כי כל Where יוצר אובייקט חדש של WhereSelectEnumerableIterator.

לעומת זאת IQueryable מבצע אופטימיזציה, ושולח הכל בשאילתה אחת.
 

Sea Bass

New member
תודה רבה על הדיון המעניין.

יש הרבה מה ללמוד.
אני שנים ב .NET אבל למקומות האלה אף פעם לא הגעתי.
תודה רבה.
 
גם אני לא הגעתי להרבה מקומות

באשכול הסמוך אתה שואל על owin ועל auth, ומה אגיד לך, אני הרבה זמן ב.net ולמקומות האלו עדיין לא הגעתי :)

לפני הרבה שנים, כאשר ראיתי מישהו עובד במחשב, התפעלתי מהגאונות הגדולה: איך הוא יודע לפתוח תיקיה! וליצור קובץ, וללחוץ לחיצה ימנית>העתק>הדבק! מי גילה לו את כל הסודות האלו!
אבל ברגע שרכשתי את המיומנות ראיתי שאין בה גאונות מיוחדת.

לדוגמא, רפלקטור. עד לפני כמה חודשים חשבתי שזה דבר מסתורי שמיועד למתכנתים אגדיים. יום אחד החלטתי שאני חייב לדעת איך מחלקה מסוימת כתובה, הורדתי את הרפלקטור של טלריק, והתחלתי לבדוק.
האמן לי שזה היה קל כמעט כמו ללמוד Notepad.
 

Royi Namir

New member
רק חבל שרפלקטור לא מראה לך באמת את הקוד שנכתב באורגינל

 
למעלה