如何为日志应用程序块提供自定义连接字符串,而不是使用.config文件中的自定义连接字符串?

时间:2009-12-29 01:24:43

标签: .net configuration enterprise-library

我正在修改现有的winforms应用程序以使用Logging Application Block。由于历史原因,此应用程序从注册表获取其主数据库连接字符串,我希望日志记录应用程序块使用相同的详细信息来记录到数据库。我怎么能这样做?

我能想到的方法是:

1)创建一个新的TraceListener并实现与FormattedDatabaseTraceListener相同的功能。如果我采用这种方法,我应该从CustomTraceListener继承,如果是这样,我如何传递格式化程序的属性来使用?

2)创建一个新的ConfigurationSource,在要求数据库连接时提供不同的详细信息。所有其他请求都将传递给FileConfigurationSource,但是当询问数据库连接详细信息时,对象将从注册表中读取相应的位。

但不明显哪个更合适,或者如何去做。有什么建议吗?

我正在使用EntLib 3.1。

感谢,

-Rory

1 个答案:

答案 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类一样。

希望这对某人有所帮助。