2


0

.configファイルにあるものを使用する代わりに、ロギングアプリケーションブロックにカスタム接続文字列を提供する方法は?

Logging Application Blockを使用するように既存のwinformsアプリケーションを変更しています。 歴史的な理由から、このアプリはレジストリからメインデータベース接続文字列を取得します。また、Logging Application Blockでデータベースへのログ記録に同じ詳細を使用したいのです。 これどうやってするの?

私が考えることができるアプローチは次のとおりです。

1)新しいTraceListenerを作成し、FormattedDatabaseTraceListenerと同じ種類の機能を実装します。 このアプローチをとる場合、CustomTraceListenerから継承する必要がありますか。その場合、使用するフォーマッターの属性をどのように渡しますか?

2)データベース接続を要求されたときに異なる詳細を提供する新しいConfigurationSourceを作成します。 他のすべての要求はFileConfigurationSourceに渡されますが、データベース接続の詳細が要求されると、オブジェクトは代わりにレジストリから適切なビットを読み取ります。

しかし、どちらがより適切であるか、またはそれを行う方法については明らかではありません。 助言がありますか?

EntLib 3.1を使用しています。

ありがとう、

-Rory

1 Answer


3


私が思いついた解決策:

最初にアプローチ(1)を見ましたが、AddStoredProcedureName属性とWriteStoredProcedureName属性をカスタムトレースリスナーに渡す方法も、ConfigurationSourceを使用するファクトリメソッドを介して通常行われるため、データベースオブジェクトを作成する方法もわかりませんでした。 (Entlib以外のdbオブジェクトを使用できたと思いますが、すべてのdbロジックを書き換えるのではなく、ほとんどFormattedDatabaseTraceListenerからコピー&ペーストしたかったのです)。

だから私が来た解決策は上記の(2)に基づいています。 FileConfigurationSourceをラップする新しいIConfigurationSourceを作成しますが、GetSection( "connectionStrings")が呼び出されると、最初にConnectionStringsSectionに、ファイルからではなくレジストリから取得したカスタム接続文字列を表すConnectionStringSettingsを設定します。

public class PSConfigurationSource : IConfigurationSource
{
    ///
    /// 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.
    ///
    private const string RMDatabaseName = "RMDatabase";

    private IConfigurationSource wrappedSource;

    private ConnectionStringsSection cxnStringsSection;

    ///
    /// Creates a PSConfigurationSource based on the wrappedSource.
    ///
    ///
    public PSConfigurationSource(IConfigurationSource wrappedSource)
    {
        this.wrappedSource = wrappedSource;
    }

    ///
    /// Retrieves the specified ,
    /// unless the connectionStrings section is requested in which case our custom
    /// config section is returned, which contains our custom connection string.
    ///
    /// The name of the section to be retrieved.
    ///
    /// The specified , or  (Nothing in Visual Basic)
    ///             if a section by that name is not found.
    ///
    public ConfigurationSection GetSection(string sectionName)
    {
        if (sectionName=="connectionStrings")
        {
            EnsureConnectionStringsSectionSet();
            return cxnStringsSection;
        }
        return wrappedSource.GetSection(sectionName);
    }

    ///
    /// Sets the cxnStringsSection object, populating it with our standard connection
    /// string retrieved from the registry.
    ///
    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[] 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


    ///
    /// Adds a  to the configuration source location specified by
    ///              and saves the configuration source.
    ///
    ///
    /// If a configuration section with the specified name already exists in the location specified by
    ///              it will be replaced.
    ///
    /// The  that represents the location where
    ///             to save the updated configuration.The name by which the  should be added.The configuration section to add.
    public void Add(IConfigurationParameter saveParameter, string sectionName, ConfigurationSection configurationSection)
    {
        wrappedSource.Add(saveParameter, sectionName, configurationSection);
    }

    ///
    /// Removes a  from the configuration source location specified by
    ///              and saves the configuration source.
    ///
    /// The  that represents the location where
    ///             to save the updated configuration.The name of the section to remove.
    public void Remove(IConfigurationParameter removeParameter, string sectionName)
    {
        wrappedSource.Remove(removeParameter, sectionName);
    }

    ///
    /// Adds a handler to be called when changes to the section named  are detected.
    ///
    /// The name of the section to watch for.The handler for the change event to add.
    public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
    {
        wrappedSource.AddSectionChangeHandler(sectionName, handler);
    }

    ///
    /// Removes a handler to be called when changes to section
    ///
    /// sectionName
    ///
    ///  are detected.
    ///
    /// The name of the watched section.The handler for the change event to remove.
    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(EnvironmentAssistant.GetConfigurationSource());
            logger.LogConfigurationError(configurationException);
        }
        catch
        { }
    }

次に、コード全体を通して、デフォルトのEntLibクラスと同じように、 `MyExceptionPolicy.HandleException(ex、" policy name ")`または `MyLogger.Log(…​)`を使用します。

これがいつか誰かに役立つことを願っています。