5

首先是这个问题:

这可能吗?我从Joe Wrobel 的工作(被遗忘的Codeplex 项目的还原版)中汲取灵感。在这里,您为提供者创建配置文件,它为它创建强类型,有效地为 Profile 类创建外观。

现在是背景故事!

我真的不喜欢魔法弦。它们非常糟糕,在更新应用程序时可能会导致一些严重问题。曾使用 PHP 和 ColdFusion 等语言工作过,我知道很容易将它们放入您的应用程序中并在需要更改之前忘记它们。然后你必须追捕它们的每一个变化并相应地改变它们。

如果您遵循“开箱即用”的应用程序模板,.NET 并没有那么好。许多示例使用 web.config 中的 appsettings 来存储各种设置。这确实是一个存储的好地方,非常适合大多数应用程序。然而,当你开始直接调用这些时,问题就开始出现了——例如ConfigurationManager.AppSettings["MyAppSetting"]。然后,当您重新使用魔术字符串时,您的情况并不比 PHP 用户好。

这就是外观的用武之地。外观提供了一种从一个地方的魔术字符串创建强类型对象的方法,并让开发人员从应用程序的其余部分引用它。

现在,我不再使用 web.config 来包含我的 appsettings,而是使用数据库来保存它们。在应用程序启动时,检索名称/值组合,然后按顺序将其添加到ConfigurationManager.AppSettingsvia Set。没什么大不了的(除了我之前遇到的问题!)。

我的数据层、服务层和表示层可以访问这个“应用程序外观”,并保存诸如应用程序模式、使用 yada yada yada 的服务端点之类的东西,并限制了必须寻找许多魔术字符串的需要,减少到两个魔术字符串 - 一个(名称)在外观中,另一个(名称和值)在创建点(对我来说是 db)。

这个外观类最终会变得相当大,我最终会厌倦不得不更新它们。

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

4

3 回答 3

8

您也可以为此目的使用 CodeSmith 模板。优点是您可以在模板文件中设置要在每次构建时重新生成的属性(设置 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 文件并包含到您要放置生成文件的项目中。要在每个构建上重新生成类,请在此处查看我的答案 模板从值中识别字符串、日期时间、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

#>
于 2010-07-31T20:41:07.370 回答
1

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

一种稍微手动但可能更方便的方法是创建一个T4 模板并将其包含在您的项目中。但是,您需要记住每次添加新设置时都重新转换模板。这会不会太繁重了?

于 2009-10-18T01:04:11.207 回答
1

@Cheburek 干得

这是 C# 端口

<#@ 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" #>
<#@ template language="C#" debug="True" hostspecific="True"  #>
<#@ output extension=".Generated.cs" #>
<#
    var projectNamespace = "SandBoxLib";
    var className  = "AppSettings";
    var fileName  = "app.config";

    Init(fileName);

#>
//------------------------------------------------------------------------------
// FileName = <#= path #>
// Generated at <#= DateTime.UtcNow.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;

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() #>        }

<#= RenderApplicationSettings() #>  }
}

<#+ 
    private string path = "";
    private XDocument doc;

    public void Init(string fileName){
        try{
            path = Host.ResolvePath(fileName);
            if (File.Exists(path)){
                doc = XDocument.Load(path);
            }
        }
        catch{
            path = "<< App.config or Web.config not found within the project >>";
        }
    }

    public string AddToCostructor(){
        if (doc == null) return "";

        var sb = new StringBuilder();

        foreach (var elem in doc.Descendants("appSettings").Elements()){
            var key = GetAttributeValue(elem, "key");
            var val = GetAttributeValue(elem, "value");
            sb.Append("\t").Append("\t").Append("\t");
            sb.AppendFormat("{0} = {1}( settings[\"{0}\"] );", key, GetConverter(val));
            sb.AppendLine();
        }

        return sb.ToString();
    }

    public string RenderApplicationSettings(){
        if (doc == null) return "";

        var sb = new StringBuilder();

        foreach (var elem in doc.Descendants("appSettings").Elements()){    
            var key = GetAttributeValue(elem, "key");
            var val = GetAttributeValue(elem, "value");

            sb.Append("\t").Append("\t");
            sb.AppendFormat("public static readonly {0} {1}; ", GetPropertyType(val), key);
            sb.AppendLine().AppendLine();
        }

        return sb.ToString();
    }

    public string GetConverter(string value){
        if (IsNumeric(value)) return "Convert.ToInt32";
        if (IsDate(value)) return "Convert.ToDateTime";
        if (IsBool(value)) return "Convert.ToBoolean";
        return "string";
    }

    public string GetPropertyType(string value){
        if (IsNumeric(value)) return "int";
        if (IsDate(value)) return "DateTime";
        if (IsBool(value)) return "bool";
        return "string";
    }

    private string GetAttributeValue(XElement elem, string attributeName){
        return elem.Attribute(attributeName).Value;
    }

    private bool IsNumeric(string value){
        return int.TryParse(value, out var r);
    }

    private bool IsDate(string value){
        return DateTime.TryParse(value, out var r);
    }

    private bool IsBool(string value){
        return Boolean.TryParse(value, out var r);
    }
#>
于 2021-05-05T15:27:36.333 回答