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