2009-12-29 1 views
2

로깅 응용 프로그램 블록을 사용하도록 기존 winforms 응용 프로그램을 수정합니다. 역사적인 이유로이 응용 프로그램은 레지스트리에서 기본 데이터베이스 연결 문자열을 가져오고 로깅 응용 프로그램 블록이 데이터베이스에 로깅하는 데 동일한 세부 정보를 사용하도록하고 싶습니다. 어떻게해야합니까? 내가 생각할 수있는로깅 응용 프로그램 블록에 .config 파일의 연결 문자열 대신 사용자 지정 연결 문자열을 제공하는 방법은 무엇입니까?

접근 방법은 다음과 같습니다

1) 새로운 TraceListener에서를 만들고 FormattedDatabaseTraceListener 같이 기능의 같은 종류를 구현한다. 이 방법을 사용하면 CustomTraceListener에서 상속해야합니까? 그렇다면 형식 기의 속성을 전달하는 방법은 무엇입니까?

2) 데이터베이스 연결을 묻는 메시지가 표시되면 다른 세부 정보를 제공하는 새 ConfigurationSource를 만듭니다. 다른 모든 요청은 FileConfigurationSource로 전달되지만 데이터베이스 연결 세부 정보를 묻는 메시지가 표시되면 객체는 레지스트리에서 적절한 비트를 읽습니다.

그러나 더 적절하거나 어떤 조치를 취하는지는 명확하지 않습니다. 어떤 제안?

저는 EntLib 3.1을 사용하고 있습니다.

답변

3

솔루션

-Rory

덕분에, 내가 해낸 :

가 처음에 나는 방법 (1) 바라 보았다하지만 AddStoredProcedureName 및 WriteStoredProcedureName을 전달하는 방법을 나에게 분명 아니었다 애트리뷰트를 내 사용자 지정 tracelistener에 전달하거나 데이터베이스 개체를 만드는 방법도 없으므로 일반적으로 ConfigurationSource를 사용하는 팩터 리 메서드를 통해 수행됩니다. (나는 비 Entlib db 객체를 사용할 수 있다고 생각하지만, 대부분 db 로직을 다시 작성하지 않고 FormattedDatabaseTraceListener에서 대부분 & 붙여 넣기를 복사하려고합니다.)

그래서 내가 얻은 해결책은 위의 (2)를 기반으로합니다. 나는 FileConfigurationSource를 래핑 새로운 IConfigurationSource을 만들 수 있지만 GetSection는 ("connectionStrings")이라고 할 때 먼저 ConnectionStringSettings 레지스트리에서 대신 파일에서 검색 내 사용자 지정 연결 문자열 표현과 ConnectionStringsSection 웁니다

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를 가져 왔습니다. 대신 일반 EntLib 클래스 ExceptionPolicy 및 로거를 사용하여 내가 그들 각각에 대해 내 네임 스페이스에 새로운 클래스를 생성 (예외 처리 및 로깅) 다음

public class EnvironmentAssistant 
{ 
... 
     public static IConfigurationSource GetConfigurationSource() 
     { 
      return 
       new PSConfigurationSource(
        new FileConfigurationSource(GetConfigFilePath())); 
     } 
... 
} 

을 : 내 경우에는 내가 EnvironmentAssistant라는 클래스에 넣어. 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 클래스와 동일합니다.

언제든지 누군가에게 도움이되기를 바랍니다.