代码之家  ›  专栏  ›  技术社区  ›  Dan Atkinson

自动生成强类型AppSettings类

  •  5
  • Dan Atkinson  · 技术社区  · 15 年前

    首先是问题:

    这有可能吗?我的灵感来自 Joe Wrobel's work (对遗忘者的简化) Codeplex project )在这里,您要为提供者创建您的概要文件,它还需要为它创建强类型,有效地为概要文件类创建一个外观。

    现在是幕后故事!

    我真的不喜欢 magic strings . 它们非常糟糕,在更新应用程序时可能会导致一些严重的问题。在使用过诸如php和coldfusion之类的语言之后,我知道很容易将它们放到应用程序中,并在需要更改之前将它们忘掉。然后你必须找到它们的每一个变种,并相应地改变它们。

    如果您遵循“开箱即用”的应用程序模板,.NET并没有那么好。很多例子都使用web.config中的appsettings来存储各种设置。这确实是一个很好的存储位置,对于大多数应用程序都是完美的。但是,当您开始直接调用这些函数时,问题就开始出现了,例如 ConfigurationManager.AppSettings["MyAppSetting"] . 然后,当你回到使用魔术字符串的时候,你不会比一个PHP用户更好。

    这里就是 facades 进来。Facades提供了一种在一个地方从魔术字符串创建强类型对象的方法,并且让开发人员引用来自应用程序其余部分的对象。

    现在,我不再使用web.config来包含我的appsettings,而是使用一个数据库来保存它们。在应用程序启动时,将检索名称/值组合,然后按顺序添加到 ConfigurationManager.AppSettings 通过 Set .没有什么大不了的(除了 problem 我早就来了!).

    这个“应用程序外观”可以被我的数据层、服务层和表示层访问,它包含应用程序模式,服务端点可以使用yada yada yada,并且限制了必须搜索多个魔术字符串的需要,包括两个魔术字符串-一个是外观中的(名称),另一个是采购订单中的(名称和值)。创建的int(对我来说是db)。

    这个facade类最终会变得非常大,我最终会厌倦更新这两个类。

    所以我想做的是有一个applicationFacade类,它在每次构建完成时自动生成。现在回到开始…这有可能吗?

    2 回复  |  直到 7 年前
        1
  •  7
  •   Community VonC    7 年前

    为此,您还可以使用codesmith模板。优点是可以在模板文件属性中设置,以便在每次生成时重新生成(set buildAction=“complile”)

    编辑 我也在寻找这样的解决方案。在谷歌搜索之后,我找到了生成此类的基本t4模板。 我重新设计了它,你可以在下面找到它。

    模板正在从web.config/app.config文件生成appsetting节的包装类

    假设在配置文件中有以下设置行

      <appSettings>
        <add key="PageSize" value="20" />
        <add key="CurrentTheme" value="MyFavouriteTheme" />
        <add key="IsShowSomething" value="True" />
      </appSettings>
    

    处理完模板后,您将得到以下类

    namespace MyProject.Core
    {
        /// <remarks>
        /// You can create partial class with the same name in another file to add custom properties
        /// </remarks>
        public static partial class SiteSettings 
        {
            /// <summary>
            /// Static constructor to initialize properties
            /// </summary>
            static SiteSettings()
            {
                var settings = System.Configuration.ConfigurationManager.AppSettings;
                PageSize = Convert.ToInt32( settings["PageSize"] );
                CurrentTheme = ( settings["CurrentTheme"] );
                IsShowSomething = Convert.ToBoolean( settings["IsShowSomething"] );
            }
    
            /// <summary>
            /// PageSize configuration value
            /// </summary>
            public static readonly int PageSize;
    
            /// <summary>
            /// CurrentTheme configuration value
            /// </summary>
            public static readonly string CurrentTheme;
    
            /// <summary>
            /// IsShowSomething configuration value
            /// </summary>
            public static readonly bool IsShowSomething;
    
        }
    }
    

    将以下代码保存到*.tt文件 并将生成的文件包含到您的项目中。 在每个生成上重新生成类 see my answer here 模板从值中识别字符串、日期时间、int和bool类型

    <#@ assembly name="System.Core" #>
    <#@ assembly name="System.Xml" #>
    <#@ assembly name="System.Xml.Linq" #>
    <#@ import namespace="System" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Xml.Linq" #>
    <#@ import namespace="Microsoft.VisualBasic" #>
    <#@ template language="VB" debug="True" hostspecific="True"  #>
    <#@ output extension=".Generated.cs" #>
    <#
        Dim projectNamespace as String = "MyProject.Core"
        Dim className as String = "SiteSettings"
        Dim fileName as String = "..\..\MyProject.Web\web.config"
    
        Init(fileName)  
    
    #>
    //------------------------------------------------------------------------------
    // FileName = <#= path #>
    // Generated at <#= Now.ToLocaltime() #>
    //
    // <auto-generated>
    //     This code was generated by a tool.
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    //     
    //    NOTE: Please use the Add a Reference to System.Configuration assembly if 
    //          you get compile errors with ConfigurationManager
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    using System;
    using System.Configuration;
    
    namespace <#= projectNamespace #>
    {
        /// <remarks>
        /// You can create partial class with the same name in another file to add custom properties
        /// </remarks>
        public static partial class <#= className #> 
        {
            /// <summary>
            /// Static constructor to initialize properties
            /// </summary>
            static <#= className #>()
            {
                var settings = System.Configuration.ConfigurationManager.AppSettings;
    <#= AddToCostructor(path) #>        }
    
    <#= RenderApplicationSettings(path) #>  }
    }
    
    <#+ 
        Dim path as String = ""
        Dim doc as XDocument = Nothing
    
        Public Sub Init(fileName as String)
            Try
                path = Host.ResolvePath(fileName)
                If File.Exists(path) Then
                    doc = XDocument.Load(path)
                End If
            Catch
                path = "<< App.config or Web.config not found within the project >>"
            End Try     
        End Sub
    
        Public Function AddToCostructor(ByVal path as String) as String                 
            If doc Is Nothing Then Return ""
    
            Dim sb as New StringBuilder()
    
            For Each result as XElement in doc...<appSettings>.<add>            
                sb.Append(vbTab).Append(vbTab).Append(vbTab)
                sb.AppendFormat("{0} = {1}( settings[""{0}""] );", result.@key, GetConverter(result.@value))
                sb.AppendLine()
            Next
    
            Return sb.ToString()
    
        End Function
    
        Public Function RenderApplicationSettings(ByVal path as String) as String
            If doc Is Nothing Then Return ""
    
            Dim sb as New StringBuilder()       
    
            For Each result as XElement in doc...<appSettings>.<add>    
                dim key = result.@key
                sb.Append(vbTab).Append(vbTab)
                sb.Append("/// <summary>").AppendLine()
                sb.Append(vbTab).Append(vbTab)
                sb.AppendFormat("/// {0} configuration value", key).AppendLine()            
                sb.Append(vbTab).Append(vbTab)
                sb.Append("/// </summary>").AppendLine()
                sb.Append(vbTab).Append(vbTab)
                sb.AppendFormat("public static readonly {0} {1}; ", GetPropertyType(result.@value), key)    
                sb.AppendLine().AppendLine()
            Next
    
            Return sb.ToString()
    
        End Function
    
        Public Shared Function GetConverter(ByVal prop as String) as String     
            If IsNumeric(prop) Then Return "Convert.ToInt32"
            If IsDate(prop) Then Return "Convert.ToDateTime"
            dim b as Boolean
            If Boolean.TryParse(prop, b) Then Return "Convert.ToBoolean"        
            Return ""
        End Function
    
        Public Shared Function GetPropertyType(ByVal prop as String) as String
            If IsNumeric(prop) Then Return "int"
            If IsDate(prop) Then Return "DateTime"
            dim b as Boolean
            If Boolean.TryParse(prop, b) Then Return "bool"
            Return "string"
        End Function
    
    #>
    
        2
  •  1
  •   itowlson    15 年前

    您可以通过预构建步骤来实现这一点。这相当容易——只需编写一个重新生成类的程序、脚本或模板,并在预生成事件中调用它——但在重新生成类之前,这会给您带来红色的Wigglies,并且对任何新成员都没有智能感知。

    一个稍微更手动,但可能更方便的方法是创建一个 T4 template 把它包括在你的项目中。但是,每次添加新设置时都需要记住重新转换模板。这会不会太麻烦?