עבודה עם DbContext בפרוייקט webapi

nocgod

New member
עבודה עם DbContext בפרוייקט webapi

אני מחפש מידע על איך לנהל life cycle של Dbcontext בתוכנית webapi (נטו webapi).
כרגע אני מעביר אותו ל logic layer במצאות injection והlifestyle שלו הוא PerWebRequest, כלומר הזמן חיים שלהם הוא קצר מאוד, מקסימום אורך הweb request.

אבל נוצר מצב אבסורדי שה logic שלי מלא בכל מיני עבודות וקריאות מול ה dbcontext, מה שפוגע ביכולת שלי לבדוק את הקוד על ידי unit tests (אלא אם כן אני עושה mock ל dbcontext עצמו ולהזריק אותו במקום ה dbcontext האמיתי)
איך ניתן בצורה הגיונית להפריד בין ה data access לבין הlogic מצד אחד ומצד שני להמנע ממצב של פתיחת כמה contexts במקביל בקריאה אחת, להמנע ממצב שSaveChanges לא חבוי לי, כלומר לי יש שליטה מלאה על הunit of work מתי הוא מתחיל ומתי הוא נגמר. וגם אפשרות לפתוח טרנסאקציות (כרגע לא צריך, אבל אני לא רוצה להגביל את עצמי בעתיד)

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

מהניסיון שלכם, איך עשיתם את זה בפרוייקטים שלכם?
 
מה הבעיה ביוניטטסטים?

לא הבנתי מה זה מפריע, ביוניט-טסט אתה בכלל לא מתעניין בlife cycle של האפליקציה.
אולי אתה מתכווין שאתה מריץ הרבה טסטים במקביל, וכל אחד פותח מופע חדש של הקונטקסט, וזה מפריע משום מה.
אם כך, הפתרון הוא שימוש בקונטיינר IoC כך אתה בטוח שיש לך רק מופע אחד ויחיד.

תמיד אתה יכול להגדיר אינטרפייס, שהDbContext יורש ממנו. האמת, חבל שEF לא עושה את זה כברירת מחדל, [כמו שטלריק למשל עושים בORM שלהם]
 

nocgod

New member
הבעיה בבדיקות היא יחסית מינורית

הבעיה העיקרית שכל ה data access מתבצע ישירות מהlogic layer.
כל המטרה שלי בפועל היא לנתק את התלות של ה logic layer שלי בEF
זה ינקה את הקוד של הלוגיקה וזה גם ישפר את היכולת שלי לבדוק דברים. מאוד קשה לעשות mock ל DbContext, לעומת זאת מאוד קל לעשות mock לinterface מאוד ספציפי.

אתה רוצה להגיד לי שבאמת נהוג להזריק dbcontext לתוך הלוגיקה ולהשתמש בו משם?
 
אתה צודק

להזריק את הDbContex זו פרקטיקה גרועה, אבל לדעתי היא נהוגה. כל הדוגמאות של MVC כתובות כך.

אתה יכול לממש בעצמך UnitOfWork וRepository, אבל חבל, כי EF נותן לך את זה מחוץ לקופסה.

הפתרון הפשוט הוא כזה:
public interface IUnitOfWork{
int SaveChanges();
}public interface IProductContext : IUnitOfWork{ DbSet<Product> Products { get; set; }}public class ProductContext : DbContext, IProductContext{ public DbSet<Product> Products { get; set; }}

אני מניח, [לא בדקתי] שקל לעשות mock לDbSet
 
הוספה

בדקתי וראיתי שלDbSet אין בנאי ציבורי [public ctor], ולכן אתה לא יכול לעשות מוקינג לקונטקסט עם DbSet.
אבל גיליתי דבר מעניין: בDbContext, אתה יכול להחליף את כל הDbSet בIDbSet, שזה ממשק גנרי שקל לממש.
public interface IDbSet<TEntity> : IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable where TEntity : class
 

nocgod

New member
אחד העובדים פה ניסה לעשות לזה mock והסתבך קצת

אני אתקדם עם העבודה וכשאגיע לזה אנסה לעשות את מה שציינת, תודה!
 
למעלה