存在不同装配体时的部分/扩展类或接口

问题描述

| 我使用一种方法来处理异常-在内部它会写入数据库,但是当发布到网络上时,源代码将不包含写入数据库所需的连接字符串。相反,它应该写入日志文件。 当不存在Foo.Private.dll时,是否可以容纳写入日志,而当存在Foo.Private.dll时,是否可以写入数据库
//In Foo.Public.dll assembly
public class SimpleLogWriter
{
    public virtual void LogError(Exception ex)
    {
        //Write to log file.
    }
}
...
//In Foo.Private.dll assembly
public class ExtendedLogWriter : SimpleLogWriter
{
    public override void LogError(Exception ex)
    {
        //Write to database
    }
}
我曾考虑过让两个日志类实现一个共享的接口(而不是扩展和覆盖)并创建一些工厂方法来呈现它,但是不确定如何验证程序集的存在或使用其类型而不添加引用,在这种情况下最终项目将具有循环引用。     

解决方法

        这听起来像.NET 4.0中提供的Managed Extensibility Framework(MEF)的潜在用例。     ,        我可以想到几种方法可以实现此目的。 紧密耦合 使用反射来检测DLL的存在。如果存在,请加载适当的类,并对其进行额外的调用。 为此,请使用Assembly.LoadFile,Assembly.GetType(string)和Activator.CreateInstance(type)。将新实例强制转换为抽象的基本记录器类型/接口。 这或多或少是您所描述的。不过,我不建议这样做,因为它不是很灵活,并且有不错的选择。 松耦合 创建一个接口或抽象记录器类,然后使用“依赖注入”(控制反转)将记录器注入需要进行记录的组件中。如果选择,则可以使用依赖项注入库以松散耦合的方式指定所需的实现。配置DI库以从您的额外DLL(如果存在)中加载依赖项。 Castle.Windsor具有松耦合的日志记录接口(日志记录工具),您可以在第二种选择中进行研究。 这些之间也有某种频谱。 这是将记录器作为依赖项注入的要点(尽管在此示例中我未使用任何库):
using System;
using System.IO;

public interface ILogger
{
    void WriteDebug(string debug);
    void WriteInfo(string info);
    void WriteError(string error);
}

public class NullLogger : ILogger
{
    private static ILogger instance = new NullLogger();

    // This singleton pattern is just here for convenience.
    // We do this because pattern has you using null loggers constantly.
    // If you use dependency injection elsewhere,// try to avoid the temptation of implementing more singletons :)
    public static ILogger Instance
    {
        get { return instance; }
    }

    public void WriteDebug(string debug) { }
    public void WriteInfo(string info) { }
    public void WriteError(string error) { }
}

public class FileLogger : ILogger,IDisposable
{
    private StreamWriter fileWriter;

    public FileLogger(string filename)
    {
        this.fileWriter = File.CreateText(filename);
    }

    public void Dispose()
    {
        if (fileWriter != null)
            fileWriter.Dispose();
    }

    public void WriteDebug(string debug)
    {
        fileWriter.WriteLine(\"Debug - {0}\",debug);
    }

    // WriteInfo,etc
}

public class SomeBusinessLogic
{
    private ILogger logger = NullLogger.Instance;

    public SomeBusinessLogic()
    {
    }

    public void DoSomething()
    {
        logger.WriteInfo(\"some info to put in the log\");
    }

    public ILogger Logger
    {
        get { return logger; }
        set { logger = value; }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        // You\'re free to use a dependency injection library for this,// or simply check for a DLL via reflections and load a logger from there
        using (var logger = new FileLogger(\"logfile.txt\"))
        {
            var someBusinessLogic = new SomeBusinessLogic()
            {
                // The component won\'t know which logger it is using - it just uses it
                Logger = logger,};

            someBusinessLogic.DoSomething();
        }
    }
}
    ,        听起来,这实际上仅与创建日志编写器的方式有关。与其尝试根据是否存在DLL来尝试进行操作,不如让该类实例化为整体配置的一部分。让用户(我的意思是安装它的人)如果愿意的话,请指定
ExtendedLogWriter
,如果他们拥有Foo.Private.dll,则指定
SimpleLogWriter
。有各种IoC容器可以简化这一过程。     ,        这可以通过使用控制反转来解决。 具有LogError(Exception)方法的接口IErrorLogger通过以下方式实现: DbErrorLogger FileErrorLogger 使用诸如Castle Windsor之类的控制API的倒置,您可以不同地配置IErrorLogger组件,因为ASP.NET 4.0具有Web.debug.config和Web.release.config,允许配置某些Web应用程序以进行调试和发布到Web场景。 最终,当您将编译更改为发布时,FileErrorLogger将成为IErrorLogger实现。 通过以下链接了解更多信息:http://docs.castleproject.org/Windsor.MainPage.ashx