باستخدام Ninject (أو بعض الحاوية الأخرى) كيف يمكنني معرفة النوع الذي يطلب الخدمة؟

StackOverflow https://stackoverflow.com/questions/719346

سؤال

افترض أن لدي واجهة لخدمة:

public interface IFooService
{
   void DoSomething();
}

وتنفيذ ملموس لتلك الخدمة التي هي عامة:

public class FooService<TRequestingClass> : IFooService
{
   public virtual void DoSomething() { }
}

ولدي بعض الطبقة الأخرى التي تحتاج إلى مثيل iFooservice:

public class Bar
{
   private IFooService _fooService;
   public Bar(IFooService fooService)
   {
      this._fooService = fooService;
   }
}

أحتاج إلى الأسلاك حاوية IOC الخاصة بي بحيث عند إنشاء شريط، يتم تمرير حجة منشئ من fooserviceu003CBar> وبعد هناك العديد من الطبقات الأخرى تماما مثل البار. قد يحتاج كل منها أيضا إلى مثيل fooserviceu003CTRequestingClass> مرت إليهم حيث يتم التعامل مع النصوص هو نوع الفصل الذي يحتاج إلى مثيل iFooservice. لست بحاجة إلى فضح هذا Quirkiness للمستهلكين في iFooservice. كل ما يجب أن يهتم به هو أنه يمكنهم الاتصال بطرق iFooservice التي تم تمريرها. يجب ألا يحتاجوا إلى معرفة أن التنفيذ الملموس ل ifooservice تم تمريره يحتاج إلى أي شيء خاص يتم بناؤه.

بديل مقبول إلى fooserviceu003CT> ستكون فئة غير عامة تحتوي على حجة سلسلة في بناءها الذي يحتوي على اسم الفصل الذي يتم إنشاؤه ل. بمعنى آخر:

public class FooService : IFooService
{
   public FooService(string requestingClassName) { }
}

كيف يمكنني الحصول على حاوية IOC الخاصة بي لبناء التبعية بهذه الطريقة؟

إذا كنت مرتبكا، فلماذا أريد مثل هذه الهيكل الغربي، فكر في كيفية عمل Log4net بشكل أفضل عند الحصول على ILOG الذي يتم إنشاؤه باستخدام Log4net.logmanager.getlogger (TypeOf (Someclass)). لا أريد القمامة من التعليمات البرمجية الخاصة بي مع الإشارات إلى log4net، لذلك أود أن أكتب واجهة iLogger بسيطة، وتنفيذها بشيء مثل هذا:

public class GenericLogger<T> : ILogger
{
    private readonly ILog log;

    public GenericLogger()
    {
        this.log = log4net.LogManager.GetLogger(typeof(T));
    }

    public void Debug(object message)
    {
        this.log.Debug(message);
    }

    /* .... etc ....  */
}
هل كانت مفيدة؟

المحلول

أسهل طريقة ستكون لإنشاء ILogger<T> واجهه المستخدم:

public class ILogger<T> : ILogger { }
public class GenericLogger<T> : ILogger<T> { ... }

ثم الاعتماد على استنتاج النوع العام للحصول على النوع الصحيح. على سبيل المثال، في Ninject، التجليد التالي هو كل ما تحتاجه:

Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>));

ثم يبدو أنواعك المستهلكة مثل هذا:

public class FooService : IFooService {
  public FooService(ILogger<FooService> logger) { ... }
}

إذا كنت عديا ضد ILogger<T> واجهة، يمكنك القيام بشيء أكثر إبداعا، مثل مزود مخصص، يقرأ IContext لتحديد نوع الأصل.

public class GenericLogger : ILogger {
  public class GenericLogger(Type type) { ... }
}

public class LoggerProvider : Provider<ILogger> {
  public override ILogger CreateInstance(IContext context) {
    return new GenericLogger(context.Target.Member.ReflectedType);
  }
}

ثم الأنواع المستهلكة ستعمل مثل هذا:

public class FooService : IFooService {
  public FooService(ILogger logger) { ... }
}

نصائح أخرى

إذا لم أكن أسيء فهمك، فلماذا لا يكون لديك ببساطة منشئ Geryerlogger الخاص بك معلمة هو نوع الكائن. ثم القيام بذلك:

ILog = kernel.Get<ILog>(ParameterList);

لم أقرأ تماما حتى الآن على قوائم المعلمة Ninject، ولكن يبدو أنه وسيلة لحقن نوع المعلمة باستخدام قائمة iParameter.

تعديل:

يبدو أن هذا سيعمل مثل هذا:

ILog = kernel.Get<ILog>(new ConstructorArgument[] { 
    new ConstructorArgument("ClassName", this.GetType().Name) 
})

ثم لديك ilog الخاص بك

class GenericLogger : Ilog
{
    GenericLogger(string ClassName) {};
}

لم أقم باختبار هذا، فقط ما يبدو أنه من مصدر Ninject (أنا أبحث عن شجرة متئناف حديثة)

تعديل:

تريد تمرير البناء، وليس المعلمة. تحديث للتفكير.

يطبع الرمز هذا: "تم استدعاؤه من INIT"

class Init {
    public void Run() {
        StandardKernel kernel = new StandardKernel( new MyLoader() );
        kernel.Get<Tester>( new ConstructorArgument[] { 
            new ConstructorArgument( "ClassName", 
                this.GetType().Name 
            ) 
        } );            
    }
}

public class Tester {
    public Tester(string ClassName) {
        Console.WriteLine("Called From {0}", ClassName);
    }
}

تعديل:

طريقة أخرى قد تكون مفيدة هي استخدام BIND (). إلى (). withconsultorargument () ملزمة، والتي يمكن أن تكون مفيدة عند استخدامها مع إما ملزمة ذاتية أو مع حالة. wheninjectedinto ().

لتوضيح فكرة المزود المخصص، إليك طريقة بسيطة للقيام بذلك ...

internal class LogModule : StandardModule
    {
        private class log4netILogProvider : SimpleProvider<log4net.ILog>
        {
            protected override ILog CreateInstance(IContext context)
            {
                return LogManager.GetLogger(context.Instance.GetType());
            }
        }

        public override void Load()
        {
            Bind<log4net.ILog>().ToProvider(new log4netILogProvider());
        }
    }

ثم في الطبقات التي يتم حقنها:

[Inject]
        public ILog logger { get; set; }    
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top