כיצד לעדכן ישות קיימת בדטה בייס (EF)
-
אני כותב REST API, ואני רוצה לכתוב מתודת PATCH ׁ(כלומר שמבצעת עדכון, אך לא מחייבת את השולח לשלוח את כל פרטי האובייקט, אלא רק לשלוח את הפרטים הרלוונטיים).
ניסיתי לכתוב בשני האופנים, הכל עובד טוב, אך לבסוף הדטה בייס לא מתעדכן.
אני מרגיש שחסר לי משהו, אך אינני יודע מהו[ResponseType(typeof(void))] public async Task<IHttpActionResult> PatchSynagogue(int id,Synagogue synagoge) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (id != synagoge.ID) { return BadRequest(); } db.Entry(synagoge).State = EntityState.Unchanged; //או במקום את השורה הזאת: db.Synagogues.Attach(synagoge); try { await db.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!SynagogeExists(id)) { return NotFound(); } else { throw; } } return StatusCode(HttpStatusCode.NoContent); }
כמובן שאני משתמש או בשורה 14 או ב16, אך לא בשניהם יחד.
קראתי כאן ששניהם מבצעים את אותו הדבר..
אך משום מה השינויים לא נשמרים בדטה בייס.
מה אני צריך להוסיף אחרי השורות הללו? (בהנחה שאני לא יודע איזה מאפיין המשתמש מתכנן לשנות..)
תודה רבה!
אברהםפורסם במקור בפורום CODE613 ב25/09/2016 15:42 (+03:00)
-
כן, אבל הנקודה היא שאינני יודע איזה שדות המשתמש שלח לעדכון.. (שהרי הנקודה שהוא יוכל לעדכן איזה מאפיין שהוא רוצה, בלי לעדכן את כל השדות..)
ב Microsoft.EntityFrameworkCore.DbSet יש פונקציה הנקראת Update, אולם באנטיטי הרגיל (System.Data.Entity)אין אותה..
ניסיתי ככה:db.Synagogues.Attach(synagogue); var item = await db.Synagogues.FindAsync(id); item = synagogue; try { await db.SaveChangesAsync(); }
אבל זה לא עובד..
פורסם במקור בפורום CODE613 ב25/09/2016 19:27 (+03:00)
-
מה שניסית לא אמור לעבוד, כי לא מתבצע שינוי ברשומה לאחר שהרצת את הפונקציה Attach.
מה שקורה כרגע הוא שמשתמש מזין את מה שהוא מעוניין לשנות בטופס ותשתית MVC היא זו שממפה את הערכים שבטופס לתוך הרשומה, ולכן זה לא עובד, לדעתי אתה צריך לקרוא לפונקציה Attach ואחר כך למפות בעצמך את הערכים שהמשתמש הזין בטופס לרשומה חדשה שתיצור בעצמך, ואז לשמור.
פורסם במקור בפורום CODE613 ב25/09/2016 19:55 (+03:00)
-
בקוד למעלה שורה 14 היא היפך שורה 16.
כשיש אובייקט אנטיטי שנטען מהDB הוא "מחובר" לContextDb שטען אותו (כל עוד הוא "חי"). ביצוע שינוי באובייקט הזה מסמן אצל הContextDb שלאנטיטי הזה יש לבצע Update בעת הSaveChanges.
כמובן שבמידה ואנטיטי בכלל לא נטען ע"י מופע הContextDb (או ממופע אחר מnew רגיל כבדוגמה שלנו - הaps עושה זאת) אז אין לו שום התייחסות ופעולה בעת הSaveChanges. הפעולה db.Synagogues.Attach או השמה של כל ערך ב EntityState ([u:1titfp79]חוץ מUnchanged[/u:1titfp79]), מחברת אנטיטי ממקור זר לDbContext הנוכחי, ומתחילה "לעקוב" אחריו. השמה של הערך Modified מספרת לContextDb שהאנטיטי שונה מהמקור בdb ולכן יש להחיל עליו משפט Update בעת השמירה על אף שלא ישתנה מכאן ולהבא, וזה מה שצריך לעשות במקרה שלך. מאיפה הוא יידע מה לעדכן? הוא לא יידע ויעדכן הכל. ותרגיש בנוח עם זה.
הערך Unchanged פועל להפך, הוא מנתק קשר בין הDb לבין אנטיטי שנטען על ידו או שקושר אליו באחת המתודות המתוארות לעיל.פורסם במקור בפורום CODE613 ב25/09/2016 20:30 (+03:00)
-
תודה על ההסבר המפורט!
הענין הוא שחשבתי שאפשר לשלוח לשרת רק פרמטר אחד והוא יעדכן רק את הפרמטר הזה.
אבל אני מבין שא"א (בצורה פשוטה, כי אפשר בצורה מסובכת לבדוק איזה פרמטר נשלח ורק אותו לעדכן..) כי ברגע שאני אומר לו לעדכן, אז הוא מעדכן את כל השדות, וכל שאר השדות הערך שלהם null או ריק, וממילא גם הם מתעדכנים..פורסם במקור בפורום CODE613 ב25/09/2016 21:50 (+03:00)
-
בדוגמאות הרשמיות ואף בסקאפאולדינג הנוצרים דינמית מתבצעת טעינה מהDB לפי הID ורק אז קישור למודל של ערכי הטופס, ראה פה: http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application תחת המקטע Update the Edit HttpPost Method.
בדרך זו המאפיינים שאכן לא שונו לא עוברים עדכון חוזר.פורסם במקור בפורום CODE613 ב26/09/2016 07:46 (+03:00)
-
בקישור שהבאת, בקטע האפור הם מסבירים שביישומי אינטרנט מכיון שאופי העבודה הוא ע"י בקשות שרת, א"א לשמור על החיבור לדטה בייס פתוח, ולכן א"א לדעת מה השתנה מהמקור, לכן תמיד פעולת עדכון תעדכן את כל השדות הקיימים.
האפשרות היחידה לפתור זאת היא ע"י שמירת שאר המאפיינים שלא שונו, כגון ע"י שדה מוסתר בטופס, וכששולחים את הטופס לכלול אף אותם, כך שבעצם אנו מעדכנים את כל האובייקט, אלא שהמשתמש יכול לערוך רק חלק מהשדות וחלק הוא בכלל לא רואה שהם קיימים.
הנה ציטוט:If you want the SQL Update statementto update only the fields that the user actually changed, you can save the original values in some way (such as hidden fields) so that they are available when the HttpPost Edit method is called. Then you can create a Student entity using the original values, call the Attach method with that original version of the entity, update the entity's values to the new values, and then call SaveChanges. For more information, see Entity states and SaveChanges and Local Data in the MSDN Data Developer Center.
אני חיפשתי אפשרות לשלוח רק מאפיין אחד, ולעדכן רק אותו ללא צורך בשליחת שאר הפרמטרים כשדות מוסתרים..
פורסם במקור בפורום CODE613 ב26/09/2016 12:40 (+03:00)
-
מעניין מאוד.
כי אתמול בדקתי עם EF CORE ומאפיין שלא שונה (אף שהוצב בו הערך שוב!) לא עובר עדכון... ממילא אותו הקוד שבדוגמה שם ייצור משפט SQL כפי שבדיוק נחוץ.
כפי הנראה של EF6 אין את היכולת הזו.
לגבי מה שהרגעתי על עדכון מיותר של שדות מצאתי פה סימוכין לכך: http://codereview.stackexchange.com/a/37306פורסם במקור בפורום CODE613 ב26/09/2016 14:47 (+03:00)