自动生成一个强类型的AppSettings类(Auto-generate a strongly-ty

2019-08-19 20:28发布

这里的问题第一:

这可能吗? 我正在从我的灵感乔·罗贝尔的工作 (被遗忘的终极版CodePlex项目 )。 在这里,你做你的是从运营商创建个人资料的工作,它也为它创造的强类型,有效地创建配置文件类门面的跑腿。

而现在后面的故事!

我真的不喜欢魔术字符串 。 他们是非常糟糕的,当它涉及到更新您的应用程序可能会导致一些严重的问题。 像PHP和ColdFusion语言工作过,我知道这很容易把它们放进你的应用程序,而忘记了他们,直到你需要改变之一。 然后,你还要对他们的每一个变化追捕,并相应地改变它们。

.NET真的是不是好多了,如果你遵循“开箱即用”应用程序模板。 的例子很多在那里使用的AppSettings在web.config中存储的各种设置。 这确实是一个好地方来存储,并且非常适合大多数应用。 问题开始然而出现,当你开始直接调用这些-例如ConfigurationManager.AppSettings["MyAppSetting"] 那么你有没有真正又比PHP用户的任何更好,因为你回用魔法字符串。

这是外墙进来。外立面提供在一个地方建立从魔法串一个强类型的对象,并具有开发者参考的一种方式,从应用程序的其余部分。

现在,而不是使用web.config中包含我的AppSettings,我使用一个数据库来保存它们。 在应用程序启动时,名称/值连击被检索,然后被顺序添加到ConfigurationManager.AppSettings经由Set 。 没什么大不了的(除了问题,我在前面了!)。

这种“应用门面”是我的数据层,服务层和表现层访问,并持有之类的应用模式,使用亚达内容十分重要,其服务端点,并限制了需要具有追捕许多神奇的字符串,下至两个法宝字符串 - 一个(名字)在门面,并在创建点其他(名称和值)(其中,对我来说是DB)。

这个外观类最终会得到相当大的,我最终会厌倦不必更新他们两个。

所以我想要做的是有一个ApplicationFacade类自动生成每次构建完成时间。 现在回到起点......这可能吗?

Answer 1:

您也可以使用CodeSmith中模板用于此目的。 好处是,你可以在模板文件属性设置为在每个版本再生(设置BuildAction的=“请编译”)

编辑我也找了这样的解决方案。 谷歌搜索后,我发现基地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文件 ,包括对你的项目,你想要把生成的文件。 再生每个build类看到我的答案在这里模板从数值识别字符串,日期时间,int和布尔类型

<#@ 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

#>


Answer 2:

你可以用预生成步骤做到这一点。 这是相当容易的事情 - 只写一个程序或脚本或模板重新生成类,并把它在你的预生成事件 - 但是这会给你红wigglies并没有智能感知任何新成员,直到类获取再生。

一个稍微手册,但可能更方便,方法是创建一个T4模板,并包括在您的项目。 然而,你需要记住每次添加一个新的设置时间重新改造的模板。 这将是过于繁重?



文章来源: Auto-generate a strongly-typed AppSettings class