سؤال

أنا مهتم بمعرفة المزيد حول كيفية حقن الأشخاص الذين يضخون التسجيل باستخدام منصات حقن التبعية. على الرغم من أن الروابط أدناه والأمثلة الخاصة بي تشير إلى Log4Net و Unity ، إلا أنني لن أستخدم بالضرورة أيًا من هؤلاء. بالنسبة لحقن التبعية/IOC ، من المحتمل أن أستخدم MEF لأن هذا هو المعيار الذي يستقر عليه بقية المشروع (كبير).

أنا جديد جدًا على حقن التبعية/IOC وأنا جديد جدًا على C# و .NET (كتبت رمز إنتاج قليل جدًا في C#/. بعد السنوات العشر الماضية أو نحو ذلك من VC6 و VB6). لقد أجريت الكثير من التحقيقات في حلول قطع الأشجار المختلفة الموجودة هناك ، لذلك أعتقد أن لدي مقبض لائق على مجموعات الميزات الخاصة بهم. أنا لست على دراية بما فيه الكفاية مع الميكانيكا الفعلية للحصول على حقن التبعية واحدة (أو ، ربما أكثر "بشكل صحيح" ، الحصول على نسخة مجردة من الحقن التبعية واحد).

لقد رأيت منشورات أخرى تتعلق بتسجيل و/أو حقن التبعية مثل:حقن التبعية وواجهات التسجيل

تسجيل أفضل الممارسات

كيف ستبدو فئة Wog4Net Wrapper؟

مرة أخرى حول log4net و Unity IOC config

لا يتعلق سؤالي بالتحديد بـ "كيفية حقن منصة التسجيل XXX باستخدام أداة IOC yyy؟" بدلاً من ذلك ، أنا مهتم بكيفية تعامل الناس مع تغليف منصة التسجيل (كما هو الحال في كثير من الأحيان ، ولكن لا ينصح دائمًا) والتكوين (أي app.config). على سبيل المثال ، باستخدام log4net كمثال ، يمكنني تكوين (في app.config) عددًا من عمليات تسجيل الدخول ثم الحصول على هذه الأشجار (بدون حقن التبعية) بالطريقة القياسية لاستخدام رمز مثل هذا:

private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

بدلاً من ذلك ، إذا لم يتم تسمية المسجل الخاص بي لفصل ، بل لمنطقة وظيفية ، يمكنني القيام بذلك:

private static readonly ILog logger = LogManager.GetLogger("Login");
private static readonly ILog logger = LogManager.GetLogger("Query");
private static readonly ILog logger = LogManager.GetLogger("Report");

لذلك ، أعتقد أن "متطلبات" ستكون شيئًا من هذا القبيل:

  1. أود أن أعزل مصدر المنتج الخاص بي من التبعية المباشرة على منصة تسجيل.

  2. أود أن أكون قادرًا على حل مثيل لوجبر محدد محدد (ربما يشارك نفس الحالة بين جميع المطلبين من نفس المثيل المسماة) إما بشكل مباشر أو غير مباشر عن طريق نوع من حقن التبعية ، وربما MEF.

  3. لا أعرف ما إذا كنت سأسمي هذا شرطًا صعبًا ، لكني أرغب في القدرة على الحصول على مسجل مسمى (مختلف عن مسجل الفصل) عند الطلب. على سبيل المثال ، قد أقوم بإنشاء مسجل لفصلي استنادًا إلى اسم الفصل ، لكن إحدى الطرق تحتاج إلى تشخيصات ثقيلة أرغب في التحكم فيها بشكل منفصل. بمعنى آخر ، قد أرغب في "تعتمد" على فئة واحدة على حالتين مسجلتين منفصلتين.

لنبدأ بالرقم 1. لقد قرأت عددًا من المقالات ، هنا في المقام الأول على Stackoverflow ، حول ما إذا كانت فكرة جيدة أم لا. شاهد رابط "أفضل الممارسات" أعلاه وانتقل إلى جيفري هانتينتعليق على عرض واحد حول سبب كونه سيئًا لفريق log4net. إذا قمت بالالتفاف (وإذا أمكنك الالتفاف بفعالية) ، فهل يمكنك التفاف بدقة لغرض الحقن/إزالة الانفصال المباشر؟ أو هل ستحاول أيضًا تجريد بعض أو كل معلومات log4net App.Config؟

دعنا نقول أنني أرغب في استخدام System.Diagnostics ، ربما أرغب في تنفيذ مسجل يعتمد على الواجهة (ربما حتى باستخدام واجهة Ilogger/ILOG "المشتركة") ، وربما بناءً على ثبات ، حتى أتمكن من ضخه. هل يمكنك تنفيذ الواجهة ، على سبيل المثال ، واستخدام معلومات System.Diagnostics App.Config كما هي؟

شيء من هذا القبيل:

public class MyLogger : ILogger
{
  private TraceSource ts;
  public MyLogger(string name)
  {
    ts = new TraceSource(name);
  }

  public void ILogger.Log(string msg)
  {
    ts.TraceEvent(msg);
  }
}

واستخدمها مثل هذا:

private static readonly ILogger logger = new MyLogger("stackoverflow");
logger.Info("Hello world!")

الانتقال إلى الرقم 2 ... كيفية حل مثيل لوجبر محدد؟ هل يجب علي الاستفادة من معلومات App.Config الخاصة بمنصة التسجيل التي أختارها (أي حل سجلات تسجيل الدخول بناءً على مخطط التسمية في App.Config)؟ لذلك ، في حالة log4net ، هل يمكنني "حقن" LogManager (لاحظ أنني أعلم أن هذا غير ممكن لأنه كائن ثابت)؟ يمكنني لف logmanager (نسميها mylogmanager) ، وإعطائها واجهة ilogmanager ، ثم حل واجهة mylogmanager.ilogmanager. يمكن أن يكون لكائناتي الأخرى رفقة (استيراد في لغة MEF) على Ilogmanager (تصدير من التجمع حيث يتم تنفيذها). الآن يمكن أن يكون لدي أشياء مثل هذا:

public class MyClass
{
  private ILogger logger;
  public MyClass([Import(typeof(ILogManager))] logManager)
  {
    logger = logManager.GetLogger("MyClass");
  }
}

في أي وقت يتم استدعاء Ilogmanager ، فإنه من المفترض مباشرة إلى LogManager من Log4Net. بدلاً من ذلك ، هل يمكن أن يأخذ LogManager ملفوف مثيلات Ilogger التي تحصل عليها بناءً على app.config وإضافتها إلى حاوية MEF (A؟) بالاسم. في وقت لاحق ، عند طلب تسجيل يحمل نفس الاسم ، يتم الاستعلام عن LogManager ملفوفة لهذا الاسم. إذا كان هناك ilogger ، يتم حله بهذه الطريقة. إذا كان هذا ممكنًا مع MEF ، فهل هناك أي فائدة تفعل ذلك؟

في هذه الحالة ، في الحقيقة ، يتم "حقن" Ilogmanager فقط ، ويمكنه توزيع مثيلات Ilogger بالطريقة التي تعمل بها log4net عادة. كيف يقارن هذا النوع من الحقن (بشكل أساسي من المصنع) لحقن مثيلات المسجل المسماة؟ هذا يسمح بزيادة الاستفادة من ملف app.config الخاص بـ Log4Net (أو منصة تسجيل أخرى).

أعلم أنه يمكنني الحصول على مثيلات من حاوية MEF مثل هذا:

var container = new CompositionContainer(<catalogs and other stuff>);
ILogger logger = container.GetExportedValue<ILogger>("ThisLogger");

ولكن كيف يمكنني الحصول على الحالات المسماة في الحاوية؟ أعرف عن النموذج القائم على السمات حيث يمكن أن يكون لدي تطبيقات مختلفة من Ilogger ، تم تسمية كل منها (عبر سمة MEF) ، لكن هذا لا يساعدني حقًا. هل هناك طريقة لإنشاء شيء مثل تطبيق. هل يمكن/يجب أن يكون هناك "مدير" مركزي (مثل Mylogmanager) يحل Loggers المسمى عبر App.Config الأساسي ثم إدراج المسجل الذي تم حله في حاوية MEF؟ وبهذه الطريقة ، سيكون متاحًا لشخص آخر لديه إمكانية الوصول إلى حاوية MEF نفسها (على الرغم من أنه بدون معرفة MyLogmanager بكيفية استخدام معلومات app.config الخاصة بـ log4net ، يبدو أن الحاوية لن تكون قادرة على حل أي سجلات مسماة مباشرة).

وقد أصبح هذا بالفعل طويلا جدا. آمل أن يكون متماسكًا. لا تتردد في مشاركة أي معلومات محددة حول كيفية حقن التبعية منصة تسجيل (على الأرجح نحن نفكر في Log4Net أو NLOG أو شيء (نأمل أن يكون رفيعًا) مبني على النظام. التشخيص) في تطبيقك.

هل قمت بحقن "المدير" وقمت بإرجاع مثيلات المسجل؟

هل قمت بإضافة بعض معلومات التكوين الخاصة بك في قسم التكوين الخاص بك أو في قسم التكوين الخاص بنظام DI الخاص بك لتسهيل/إمكانية حقن مثيلات المسجل مباشرة (أي اجعل تبعياتك على Ilogger بدلاً من Ilogmanager).

ماذا عن وجود حاوية ثابتة أو عالمية تحتوي على واجهة Ilogmanager فيه أو مجموعة من مثيلات Ilogger المسماة فيه. لذلك ، بدلاً من الحقن بالمعنى التقليدي (عبر مُنشئ أو خاصية أو بيانات الأعضاء) ، يتم حل التبعية في التسجيل بشكل صريح عند الطلب. هل هذه طريقة جيدة أو سيئة لحقن التبعية.

أنا أميز هذا على أنه ويكي مجتمع لأنه لا يبدو وكأنه سؤال مع إجابة محددة. إذا شعر أي شخص بخلاف ذلك ، فلا تتردد في تغييره.

شكرا على اي مساعدة!

هل كانت مفيدة؟

المحلول 3

هذا لصالح أي شخص يحاول معرفة كيفية حقن تبعية المسجل عندما يتم تزويد المسجل الذي تريد حقنه منصة تسجيل مثل Log4Net أو NLOG. كانت مشكلتي هو أنني لم أستطع أن أفهم كيف يمكنني جعل الفصل (على سبيل المثال myClass) يعتمد على واجهة من نوع Ilogger عندما عرفت أن دقة Ilogger المحددة تعتمد على معرفة نوع الفصل الذي يعتمد على Ilogger ( على سبيل المثال myclass). كيف تحصل منصة/حاوية DI/IOC على Ilogger المناسب؟

حسنًا ، لقد نظرت إلى مصدر القلعة والتسعة ورأيت كيف يعملون. لقد نظرت أيضًا إلى autofac و structuremap.

يوفر القلعة والتسعة تنفيذًا للتسجيل. كلا دعم log4net و nlog. القلعة تدعم أيضا System.Diagnostics. في كلتا الحالتين ، عندما يحل النظام الأساسي التبعيات لكائن معين (على سبيل المثال عندما يقوم النظام الأساسي بإنشاء myClass ويعتمد myClass على Ilogger) ، فإنه ينفصل عن إنشاء التبعية (Ilogger) إلى "مزود" Ilogger مصطلح مشترك). يكون تنفيذ مزود Ilogger مسؤولاً عن إنشاء مثيل Ilogger فعليًا وتسليمه مرة أخرى ، ليتم حقنه بعد ذلك في الفئة المعتمدة (مثل MyClass). في كلتا الحالتين ، يعرف المزود/المحلول نوع الفئة التابعة (مثل MyClass). لذلك ، عندما يتم إنشاء MyClass ويتم حل تبعياتها ، يعرف Ilogger "Resolver" أن الفصل هو myClass. في حالة استخدام القلعة أو حلول تسجيل التسجيل المقدمة من Ninject ، فإن هذا يعني أن حل التسجيل (الذي تم تنفيذه كجليف على log4net أو nlog) يحصل على النوع (myClass) ، بحيث يمكنه التفويض إلى log4net.logmanager.getLogger () أو nlog.logmanager.getLogger (). (لست متأكدًا بنسبة 100 ٪ من بناء الجملة لـ log4net و nlog ، لكنك تحصل على الفكرة).

على الرغم من أن AutofAC و structuremap لا يوفرون منشأة تسجيل (على الأقل يمكنني أن أقول من خلال البحث) ، يبدو أنهما يوفران القدرة على تنفيذ المحللين المخصصين. لذلك ، إذا كنت ترغب في كتابة طبقة تجريد التسجيل الخاصة بك ، فيمكنك أيضًا كتابة حل مخصص مقابل. وبهذه الطريقة ، عندما ترغب الحاوية في حل Ilogger ، سيتم استخدام حديدك للحصول على Ilogger الصحيح وسيكون لديه الوصول إلى السياق الحالي (أي ما هي تبعيات الكائن التي يتم استيفاءها حاليًا - ما هو الكائن الذي يعتمد على Ilogger). احصل على نوع الكائن ، وأنت على استعداد لتفويض إنشاء Ilogger إلى منصة التسجيل التي تم تكوينها حاليًا (والتي ربما تكون قد استخلصتها خلف واجهة والتي كتبت فيها حلًا).

لذلك ، هناك حاجة إلى بعض النقاط الرئيسية التي اشتبهت ، لكنني لم أفهم تمامًا من قبل:

  1. في النهاية ، يجب أن تكون حاوية DI على دراية ، بطريقة ما ، بما يجب استخدامه منصة التسجيل. عادةً ما يتم ذلك عن طريق تحديد أن "Ilogger" يجب حلها بواسطة "حل" خاص بمنصة تسجيل (وبالتالي ، تحتوي Castle على Log4Net و NLOG و System.Diagnostics "Resolvers" (من بين آخرين)). يمكن إجراء مواصفات المحلول التي يجب استخدامها عبر ملف التكوين أو برمجيًا.

  2. يحتاج المحلل إلى معرفة السياق الذي يتم حل التبعية (Ilogger). هذا هو ، إذا تم إنشاء MyClass ويعتمد على Ilogger ، فحينئذٍ عندما يحاول المحلل إنشاء Ilogger الصحيح ، يجب أن يعرف (Resolver) النوع الحالي (MyClass). وبهذه الطريقة ، يمكن للمحلول استخدام تطبيق التسجيل الأساسي (Log4Net ، NLOG ، إلخ) للحصول على المسجل الصحيح.

قد تكون هذه النقاط واضحة لأولئك مستخدمي DI/IOC هناك ، لكنني الآن أتيحها الآن ، لذلك استغرق الأمر بعض الوقت للحصول على رأسي حوله.

شيء واحد لم أحصل عليه بعد هو كيف أو إذا كان هناك شيء مثل هذا ممكن مع MEF. هل يمكنني الحصول على كائن يعتمد على واجهة ثم هل يتم تنفيذ التعليمات البرمجية الخاصة بي بعد أن قام MEF بإنشاء الكائن ، وبينما يتم حل الواجهة/التبعية؟ لذا ، افترض أن لدي فئة مثل هذا:

public class MyClass
{
  [Import(ILogger)]
  public ILogger logger;

  public MyClass()
  {
  }

  public void DoSomething()
  {
    logger.Info("Hello World!");
  }
}

عندما تقوم MEF بحل الواردات الخاصة بـ MyClass ، هل يمكنني الحصول على بعض الكود الخاص بي (عبر سمة ، عبر واجهة إضافية حول تنفيذ Ilogger ، في مكان آخر ؟؟؟) تنفيذ وحل استيراد Ilogger بناءً على حقيقة أنه MyClass التي هي حاليًا في السياق وتُعيد (محتمل) مثيل Ilogger مختلفًا مما سيتم استرداده من أجل yourclass؟ هل أقوم بتنفيذ نوع من مزود MEF؟

في هذه المرحلة ، ما زلت لا أعرف عن MEF.

نصائح أخرى

أنا أستخدم Ninject لحل اسم الفئة الحالي لمثيل Logger مثل هذا:

kernel.Bind<ILogger>().To<NLogLogger>()
  .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName);

يمكن أن يبدو مُنشئ تطبيق NLOG مثل هذا:

public NLogLogger(string currentClassName)
{
  _logger = LogManager.GetLogger(currentClassName);
}

يجب أن يعمل هذا النهج مع حاويات IOC الأخرى أيضًا ، كما أعتقد.

يمكن للمرء أيضًا استخدامه المشترك واجهة أو واجهة تسجيل بسيطة.

كلاهما يستخدم نمط نمط محدد موقع الخدمة لاسترداد ilogger.

بصراحة ، يعد التسجيل أحد تلك التبعيات التي لا أرى سوى القليل أو معدومة في الحقن تلقائيًا.

تبدو معظم فصولي التي تتطلب خدمات التسجيل مثل هذا:

public class MyClassThatLogs {
    private readonly ILogger log = Slf.LoggerService.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);

}

من خلال استخدام واجهة التسجيل البسيطة ، قمت بتبديل مشروع من Log4Net إلى NLOG ، وقد أضفت تسجيل الدخول من مكتبة طرف ثالث استخدمت Log4Net بالإضافة إلى تسجيل التسجيل الخاص بطلبي باستخدام NLOG. وهذا يعني أن الواجهة خدمتنا بشكل جيد.

أحد التحذيرات التي يصعب تجنبها هو فقدان الميزات الخاصة بإطار تسجيل واحد أو آخر ، وربما المثال الأكثر شيوعًا هو مستويات التسجيل المخصصة.

أراك اكتشفت إجابتك الخاصة :) ولكن بالنسبة للأشخاص في المستقبل الذين لديهم هذا السؤال حول كيفية عدم ربط نفسك بإطار تسجيل معين ، هذه المكتبة: المشترك يساعد بالضبط مع هذا السيناريو.

لقد صنعت عادتي ServiceExportProvider, ، من قبل المزود أقوم بتسجيل سجل log4net لحقن التبعية بواسطة MEF. ونتيجة لذلك ، يمكنك استخدام المسجل لأنواع مختلفة من الحقن.

مثال على الحقن:

[Export]
public class Part
{
    [ImportingConstructor]
    public Part(ILog log)
    {
        Log = log;
    }

    public ILog Log { get; }
}

[Export(typeof(AnotherPart))]
public class AnotherPart
{
    [Import]
    public ILog Log { get; set; }
}

مثال على الاستخدام:

class Program
{
    static CompositionContainer CreateContainer()
    {
        var logFactoryProvider = new ServiceExportProvider<ILog>(LogManager.GetLogger);
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        return new CompositionContainer(catalog, logFactoryProvider);
    }

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        var container = CreateContainer();
        var part = container.GetExport<Part>().Value;
        part.Log.Info("Hello, world! - 1");
        var anotherPart = container.GetExport<AnotherPart>().Value;
        anotherPart.Log.Fatal("Hello, world! - 2");
    }
}

يؤدي إلى وحدة التحكم:

2016-11-21 13:55:16,152 INFO  Log4Mef.Part - Hello, world! - 1
2016-11-21 13:55:16,572 FATAL Log4Mef.AnotherPart - Hello, world! - 2

ServiceExportProvider تطبيق:

public class ServiceExportProvider<TContract> : ExportProvider
{
    private readonly Func<string, TContract> _factoryMethod;

    public ServiceExportProvider(Func<string, TContract> factoryMethod)
    {
        _factoryMethod = factoryMethod;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
    {
        var cb = definition as ContractBasedImportDefinition;
        if (cb?.RequiredTypeIdentity == typeof(TContract).FullName)
        {
            var ce = definition as ICompositionElement;
            var displayName = ce?.Origin?.DisplayName;
            yield return new Export(definition.ContractName, () => _factoryMethod(displayName));
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top