我正在修改现有的winforms应用程序以使用Logging Application Block。由于历史原因,此应用程序从注册表获取其主数据库连接字符串,我希望日志记录应用程序块使用相同的详细信息来记录到数据库。我怎么能这样做?
我能想到的方法是:
1)创建一个新的TraceListener并实现与FormattedDatabaseTraceListener相同的功能。如果我采用这种方法,我应该从CustomTraceListener继承,如果是这样,我如何传递格式化程序的属性来使用?
2)创建一个新的ConfigurationSource,在要求数据库连接时提供不同的详细信息。所有其他请求都将传递给FileConfigurationSource,但是当询问数据库连接详细信息时,对象将从注册表中读取相应的位。
但不明显哪个更合适,或者如何去做。有什么建议吗?
我正在使用EntLib 3.1。
感谢,
-Rory
答案 0 :(得分:3)
我提出的解决方案:
最初我查看了方法(1)但是我不明白如何将AddStoredProcedureName和WriteStoredProcedureName属性传递给我的自定义tracelistener,也不知道如何创建Database对象,因为它通常是通过使用ConfigurationSource的工厂方法完成的。 (我想我可以使用非Entlib数据库对象,但我想主要从FormattedDatabaseTraceListener复制和粘贴,而不是重写所有数据库逻辑)。
所以我的解决方案基于上面的(2)。我创建了一个包装FileConfigurationSource的新IConfigurationSource,但是当调用GetSection(“connectionStrings”)时,它首先使用ConnectionStringSettings填充ConnectionStringsSection,ConnectionStringSettings表示从注册表而不是从文件中检索的自定义连接字符串:
public class PSConfigurationSource : IConfigurationSource
{
/// <summary>
/// Name of the connection string that will be set to use the standard connection
/// string from the registry. Anything wanting to reference the RM database should
/// reference this connection string name.
/// </summary>
private const string RMDatabaseName = "RMDatabase";
private IConfigurationSource wrappedSource;
private ConnectionStringsSection cxnStringsSection;
/// <summary>
/// Creates a PSConfigurationSource based on the wrappedSource.
/// </summary>
/// <param name="wrappedSource"></param>
public PSConfigurationSource(IConfigurationSource wrappedSource)
{
this.wrappedSource = wrappedSource;
}
/// <summary>
/// Retrieves the specified <see cref="T:System.Configuration.ConfigurationSection"/>,
/// unless the connectionStrings section is requested in which case our custom
/// config section is returned, which contains our custom connection string.
/// </summary>
/// <param name="sectionName">The name of the section to be retrieved.</param>
/// <returns>
/// The specified <see cref="T:System.Configuration.ConfigurationSection"/>, or <see langword="null"/> (<b>Nothing</b> in Visual Basic)
/// if a section by that name is not found.
/// </returns>
public ConfigurationSection GetSection(string sectionName)
{
if (sectionName=="connectionStrings")
{
EnsureConnectionStringsSectionSet();
return cxnStringsSection;
}
return wrappedSource.GetSection(sectionName);
}
/// <summary>
/// Sets the cxnStringsSection object, populating it with our standard connection
/// string retrieved from the registry.
/// </summary>
private void EnsureConnectionStringsSectionSet()
{
if (cxnStringsSection == null)
{
// Get the connectionStrings section from the config file.
ConfigurationSection configSection = wrappedSource.GetSection("connectionStrings");
if ((configSection != null) && (configSection is ConnectionStringsSection))
cxnStringsSection = configSection as ConnectionStringsSection;
else
cxnStringsSection = new ConnectionStringsSection();
// Add in the RM database settings. Seems that ConnectionStringSettingsCollection[<string>] doesn't have a setter,
// despite it being in the documentation, so need to remove then add in case it's already there.
cxnStringsSection.ConnectionStrings.Remove(RMDatabaseName);
cxnStringsSection.ConnectionStrings.Add(new ConnectionStringSettings(
RMDatabaseName, SomeStaticHelperClass.GetConnectionStringFromRegistry(), "System.Data.SqlClient"));
}
}
#region WrappedMethods
/// <summary>
/// Adds a <see cref="T:System.Configuration.ConfigurationSection"/> to the configuration source location specified by
/// <paramref name="saveParameter"/> and saves the configuration source.
/// </summary>
/// <remarks>
/// If a configuration section with the specified name already exists in the location specified by
/// <paramref name="saveParameter"/> it will be replaced.
/// </remarks>
/// <param name="saveParameter">The <see cref="T:Microsoft.Practices.EnterpriseLibrary.Common.Configuration.IConfigurationParameter"/> that represents the location where
/// to save the updated configuration.</param><param name="sectionName">The name by which the <paramref name="configurationSection"/> should be added.</param><param name="configurationSection">The configuration section to add.</param>
public void Add(IConfigurationParameter saveParameter, string sectionName, ConfigurationSection configurationSection)
{
wrappedSource.Add(saveParameter, sectionName, configurationSection);
}
/// <summary>
/// Removes a <see cref="T:System.Configuration.ConfigurationSection"/> from the configuration source location specified by
/// <paramref name="removeParameter"/> and saves the configuration source.
/// </summary>
/// <param name="removeParameter">The <see cref="T:Microsoft.Practices.EnterpriseLibrary.Common.Configuration.IConfigurationParameter"/> that represents the location where
/// to save the updated configuration.</param><param name="sectionName">The name of the section to remove.</param>
public void Remove(IConfigurationParameter removeParameter, string sectionName)
{
wrappedSource.Remove(removeParameter, sectionName);
}
/// <summary>
/// Adds a handler to be called when changes to the section named <paramref name="sectionName"/> are detected.
/// </summary>
/// <param name="sectionName">The name of the section to watch for.</param><param name="handler">The handler for the change event to add.</param>
public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
{
wrappedSource.AddSectionChangeHandler(sectionName, handler);
}
/// <summary>
/// Removes a handler to be called when changes to section
/// <code>
/// sectionName
/// </code>
/// are detected.
/// </summary>
/// <param name="sectionName">The name of the watched section.</param><param name="handler">The handler for the change event to remove.</param>
public void RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
{
wrappedSource.RemoveSectionChangeHandler(sectionName, handler);
}
#endregion
}
然后我使用此ConfigurationSource而不是默认值。对SomeStaticHelperClass.GetConnectionStringFromRegistry()的调用获取应用程序中其他位置使用的连接字符串。此解决方案还具有以下优点:我不需要重现FormattedDatabaseTraceListener的功能 - 即实际处理数据库逻辑。
要使用PSConfigurationSource而不是默认值,我创建了一个静态方法来获取PSConfigurationSource。在我的例子中,我把它放在一个名为EnvironmentAssistant的类中:
public class EnvironmentAssistant
{
...
public static IConfigurationSource GetConfigurationSource()
{
return
new PSConfigurationSource(
new FileConfigurationSource(GetConfigFilePath()));
}
...
}
然后我没有使用普通的EntLib类ExceptionPolicy和Logger(用于异常处理和日志记录),而是在我的命名空间中为每个类创建了新类。只需复制ExceptionPolicy和Logger的代码,然后重命名为MyExceptionPolicy和MyLogger。然后进行一些小的更改,以便使用静态方法GetConfigurationSource()
而不是使用默认配置源。
MyExceptionPolicy(Just Initialise似乎已修改。我不确定是否添加了此方法或只是修改了它。如果我添加它,那么你需要添加对它的调用作为HandleFirstException()和HandleException中的第一行( )。)
private static void Initialise()
{
if (defaultFactory == null)
{
// Nested check should mean that locking overhead isn't applied unless necessary,
// and means factory won't be overwritten if two threads hit locked section.
lock (sync)
{
if (defaultFactory == null)
{
exceptionsSource = EnvironmentAssistant.GetConfigurationSource();
defaultFactory = new ExceptionPolicyFactory(exceptionsSource);
}
}
}
}
MyLogger:
private static LogWriterFactory factory = new LogWriterFactory(EnvironmentAssistant.GetConfigurationSource());
...
internal static void TryLogConfigurationFailure(ConfigurationErrorsException configurationException)
{
try
{
DefaultLoggingEventLogger logger = EnterpriseLibraryFactory.BuildUp<DefaultLoggingEventLogger>(EnvironmentAssistant.GetConfigurationSource());
logger.LogConfigurationError(configurationException);
}
catch
{ }
}
然后在我的代码中,我使用MyExceptionPolicy.HandleException( ex, "policy name" )
或MyLogger.Log(...)
,就像使用默认的EntLib类一样。
希望这对某人有所帮助。