בעיית הזרקה עם Ninject ו WebApi

Sea Bass

New member
בעיית הזרקה עם Ninject ו WebApi

אני משתמש ב Ninject באפליקציית WebApi ויש לי בעיה שטחנתי עליה ולא הצלחתי לפתור אותה.
הבעיה היא שאותו Instance של Service מסויים שאני מזריק (נקרא לו IContext), מוזרק לי ב Relosve על אף שהוא ב Scope הדיפולטי שלו Transient.
נניח יש לי שני קלאסים: ClassA ו ClassB שמקבלים IContext ב Ctor שלהם.
בנוסף ClassB מקבל ClassA ב Ctor שלו.
כאשר מתבצע Resolve, נוצר לי Context אחד, והוא מוזרק לי גם ל ClassA וגם לClassB.
את ה IContext אני מייצר מ Provider ואני רואה שהוא נקרא פעמיים, מייצר שני Instaces שונים, אך רק הראשון מוזרק.
אני לא מבצע InRequestScope.
אני מעלה את ה WebCommon ואת ה Bindings.
מה יכולה להיות הבעיה? אני משתגע.
דרך אגב, ה Context הזה זה DBContext ואני רוצה Instance חדש לכל סרביס ופה הוא משותף לי.
אציין שאם ישנה בקשת Http נוספת, אז כמובן Instance אחר יוזרק , אבל עדיין, אותו Instance למספר Services.
תודה רבה.

קוד:
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
     
public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }

        public static void Stop()
        {
            bootstrapper.ShutDown();
        }
    
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            CreateServices(kernel);
            GlobalConfiguration.Configuration.DependencyResolver = new WebApiNinjectDependencyResolver(kernel);
            return kernel;
        }

        private static void CreateServices(IKernel Kernel)
         {
          Kernel.Bind<IContext>().ToProvider<ContextProvider>();
         }
 

nocgod

New member
אם אתה מדבר על DbContext של Entity FrameWork

אז נהוג שיהיה לו Per-Request Lifecycle (או Scoped) ובכל מקרה אתה רוצה שלאותה הבקשה יהיה את אותו הקונטקסט כדי להמנע מאירועים לא נעימים שנובעים מ contexts כפולים לאותה הבקשה.

אם אתה רוצה context שיכיל פונקציות ספציות לכל class אולי כדאי לך לנסות לייצר כמה interfaces ובמקום להזריק IDbContext להזריק IClassAContext שימומש על ידי ה DbContext שלך ו IClassBContext שגם הוא ימומש על ידי ה DbContext שלך. ואז classA יהיה תלוי ב IClassAContext שיהיה לו lifecycle משלו, ו classB יהיה תלוי ב IClassBContext שגם לו יהיה lifecycle משלו, נפרד מIClassAContext.

קוד:
interface IClassAContext
{
	A A { get; set; }
}

interface IClassBContext
{
	B B { get; set; }
}

interface IClassA{...}
interface IClassB{...}

class MyContext : DbContext, IClassAContext, IClassBContext
{
	public A A { get; set; }
	public B B { get; set; }
}
class A : IClassA
{
	public A(IClassAContext ctx) {...}
}

class B : IClassB
{
	public B(IClassA classA, IClassBContext ctx) {...}
}
 

Sea Bass

New member
אני לא רוצה פונקציות

ספציפיות לכל Class.
ב IContext יש Method של <IQueryable<T וכל הסרביסים משתמשים איתה.
כמו שה IContext הזה מוזרק לי ל IClassA ו IClassB יש עוד המון Services שמקבלים IContext ב Ctor.
אוסיף ואומר, ש לא רק ל IContext זה יוצר Instance אחד אלא לעוד Services שאני מזריק והם מוזרקים מספר פעמים.
ההתנהגות היא כמו InRequestScope שבו Instance אחד של Service מוזרק לכל מקום באותה בקשה של Http.
ואת זה אני רוצה לשנות
 

Sea Bass

New member
עדכון לבעיה

כשאני משתמש ב Lazy זה מזריק לי כמו שצריך.
לדוגמא,
עובד כמצופה
קוד:
public class MyController
{
Lazy<IClassB> _serviceB;
Public MyController(Lazy<IClassB> serviceB)
{
_serviceB = serviceB;
} 

 public IHttpActionResult MyAction
{
_ServiceB.Value.Do();
}
}

ופה, כשאין Lazy, אותו Instance מוזרק

קוד:
public class MyController
{
IClassB _serviceB;
Public MyController(IClassB serviceB)
{
_serviceB = serviceB;
} 

 public IHttpActionResult MyAction
{
_ServiceB.Do();
}
}
 

nocgod

New member
שוב זו לא בעיה זה מה שאמרת לו לעשות

אם אתה רוצה שה context יהיה ב per-request אז הוא יוזרק לכל הservices שקשורים לאותו ה request
על מנת להמנע מזה אתה צריך לעשות אותו transient ואז הוא יוזרק חדש לכל מקום
 

nocgod

New member
שים לב להערות שיש שם

שלא תדפוק משהו אחר, כי נראה מאוד
מה שאתה מנסה לעשות.
אם אין נעילה על Ninject ספציפיצ - אני מאוד אוהב את Windsor Castle, אולי לא יהיו לך בעיות איתו, או לחליפין לא תצטרך לעשות דברים מוזרים, כי זה נראה לי הזמנה לדליפות זיכרון מה שתיארו שם

אגב למה פרוייקט חדש לא מפותח בתשתית OWIN? (או שזה לא פרוייקט חדש?)
 

Sea Bass

New member
למה פישי?

זה רשום שם ברירור.
במצב הזה ל Scope אין שום משמעות וה Kernel מושמד בכל קריאה מה שגורם ל InTransientScope() או לכל Scope אחר לא לעבוד.
כמובן שלא צריך לבצע Dispose ל Kernel.
הפרוייקט מכיל OWIN שדואג לבצע Authentication. למה זה קשור ל Ninject?
*הפרוייקט Hosted על ידי IIS.
Windsor Castle? אנחנו כבר משתמשים ב Ninject.
יש כמה מקומות היסטורים בקוד שמשתמשים ב IKernel ולאט לאט אני משמיד את זה, לכן אני לא מתכוון לעבור.
 

nocgod

New member
OWIN לא קשור, סתם התעניינתי...

ולגבי קאסל - חשבתי זה פרוייקט חדש אז הצעתי לנסות, אולי הוא יספק את הסחורה, אבל כבר הסתדרת אז זה לא רלוונטי (וזה גם לא פרוייקט חדש).
 
למעלה