【问题标题】:Partial/Extended Class or Interface When Different Assembly Present存在不同程序集时的部分/扩展类或接口
【发布时间】:2011-06-01 06:19:57
【问题描述】:

我使用一种方法来处理异常 - 它在内部写入数据库,但是当发布到网络时,源代码将不包含写入数据库所需的连接字符串。相反,它应该写入日志文件。

是否可以在 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
    }
}

我曾考虑让两个日志类实现共享接口(而不是扩展和覆盖)并创建一些工厂方法来呈现它,但不确定如何验证程序集的存在或在不添加引用的情况下使用它的类型,在这种情况下,最终项目将具有循环引用。

【问题讨论】:

    标签: c# reflection partial class-extensions


    【解决方案1】:

    这听起来像是 .NET 4.0 中可用的 Managed Extensibility Framework (MEF) 的潜在用例。

    【讨论】:

    • 这是一个有趣的选项,因为(我相信)它内置于 .Net 4 中。这里有关于使用 MEF 与 IoC 库的讨论 - stackoverflow.com/questions/216565/…
    • 这看起来很有希望,但我不清楚。具有 MEF 导入的程序集是否会自动查看 bin 文件夹中的所有其他程序集以进行导出,还是您必须以某种方式指定候选程序集?
    • @lukiffer:我相信你会想要使用 DirectoryCatalog 来指定一个文件夹来探测程序集;见msdn.microsoft.com/en-us/library/…
    【解决方案2】:

    我可以想出几种方法来实现这一点。

    紧耦合

    使用反射来检测 DLL 的存在。如果存在,请加载适当的类,并对它们进行额外调用。

    为此,请使用Assembly.LoadFileAssembly.GetType(string)Activator.CreateInstance(type)。将新实例转换为您的抽象基础记录器类型/接口。

    这或多或少是您所描述的。不过我不建议这样做,因为它不是很灵活,而且有很好的替代方案。

    松散耦合

    创建一个接口或抽象记录器类,并使用Dependency Injection (Inversion of Control) 将记录器注入到需要进行记录的组件中。如果您选择,您可以使用依赖注入库以松散耦合的方式指定您想要的实现。配置 DI 库以从额外的 DLL(如果存在)加载依赖项。

    Castle.Windsor 有一个松耦合的日志接口(@98​​7654325@),您可以查看第二个选项。

    这两者之间也存在某种光谱。

    这是将记录器作为依赖项注入的要点(尽管在此示例中我没有使用任何库):

    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();
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      这听起来真的只是你如何创建日志编写器的问题。与其尝试根据是否存在 DLL 来执行此操作,不如允许将类实例化为整体配置的一部分。让用户(我的意思是安装它的人)指定ExtendedLogWriter,如果他们想要,如果他们有Foo.Private.dll,否则指定SimpleLogWriter。有各种 IoC 容器可以让这变得简单。

      【讨论】:

      • 在我们的程序集中(最终用户将添加为项目引用)将调用LogClientInstance.GetInstance().LogError(ex)。在我们的服务器上 Foo.Private.dll 将存在并写入数据库,但如果它不存在于文件中。构建最终用户的项目不应需要 Foo.Private.dll。 GetInstance() 方法返回 ILogClient,它应该是 SimpleLogWriterExtendedLogWriter 的一个实例,还是过于复杂?另外,我没有使用过 Inversion - 有什么好读的吗?
      • @lukiffer:好的,所以你基本上有一个单身......嗯。记录单例真正有用多于麻烦的少数几个地方之一,但是如果您可以在 LogClientInstance 中使用配置信息,那将是 IMO 最合适的事情......我没有任何关于控制反转的具体阅读建议,但您应该能够通过快速搜索找到任意数量的教程。真是让人大开眼界:)(也可以搜索依赖注入。)
      • 谢谢!就在您认为自己已经牢牢掌握 C# 的时候,您发现了 10 个您以前甚至没有考虑过的其他范例。 :)
      【解决方案4】:

      这可以通过使用控制反转来解决。

      带有 LogError(Exception) 方法的接口 IErrorLogger 由以下方式实现:

      • DbErrorLogger
      • 文件错误记录器

      使用诸如 Castle Windsor 之类的控制反转 API,您可以不同地配置 IErrorLogger 组件,因为 ASP.NET 4.0 具有 Web.debug.configWeb.release.config允许配置一些 Web 应用程序以进行调试和发布到 Web 场景。

      最终,当您将编译更改为发布时,FileErrorLogger 将成为 IErrorLogger 实现。

      点击此链接了解更多信息:http://docs.castleproject.org/Windsor.MainPage.ashx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-22
        • 2019-04-07
        • 2020-02-19
        • 2020-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多