2009-10-18 5 views
5

fortement typé Voici la première question:Auto-générer une AppSettings classe

Est-ce possible? Je prends mon inspiration de Joe Wrobel's work (un redux du Codeplex project oublié). Ici, vous faites votre travail sur la création de votre profil pour le fournisseur, et il fait le travail de création de la frappe forte pour cela, en créant effectivement une façade pour la classe Profile.

Et maintenant l'histoire de retour!

Je n'aime vraiment pas magic strings. Ils sont assez mauvais et peuvent causer de sérieux problèmes quand il s'agit de mettre à jour votre application. Ayant travaillé dans des langages comme PHP et ColdFusion, je sais qu'il est facile de les mettre dans votre application et de les oublier jusqu'à ce que vous en changiez un. Et puis vous devez chasser chaque variante d'entre eux et les modifier en conséquence.

.NET n'est vraiment pas bien mieux si vous suivez les modèles d'applications 'out of the box'. Beaucoup d'exemples utilisent les appsettings dans web.config pour stocker divers paramètres. C'est en effet un bon endroit pour stocker, et est parfait pour la plupart des applications. Des problèmes commencent cependant à se produire lorsque vous commencez à les appeler directement - par exemple ConfigurationManager.AppSettings["MyAppSetting"]. Alors vous n'êtes pas vraiment mieux qu'un utilisateur de PHP que vous êtes de retour à l'aide de chaînes magiques.

C'est ici qu'interviennent facades. Les façades offrent un moyen de créer un objet fortement typé à partir d'une chaîne magique en un seul endroit, et de faire référence au développeur par rapport au reste de l'application. Maintenant, au lieu d'utiliser un web.config pour contenir mes appsettings, j'utilise une base de données pour les contenir tous. Au démarrage de l'application, les combinaisons nom/valeur sont récupérées et sont ensuite ajoutées séquentiellement au ConfigurationManager.AppSettings via Set. Aucun gros (en dehors de la problem j'avais plus tôt!). Cette «façade d'application» est accessible par ma couche de données, couche de service et couche de présentation et contient des choses comme le mode application, quel point de service utilise yada yada yada et limite le besoin d'avoir à chercher de nombreuses chaînes magiques, jusqu'à deux cordes magiques - une (le nom) dans la façade, et l'autre (le nom et la valeur) dans le point de création (qui, pour moi est le db).

Cette classe de façade finira par devenir assez grande et je finirai par en avoir assez de devoir les mettre à jour tous les deux.

Donc ce que je voudrais faire est d'avoir une classe ApplicationFacade qui génère automatiquement chaque fois qu'une construction est terminée. Et maintenant, revenons au début ... Est-ce possible?

Répondre

7

Vous pouvez également utiliser des modèles CodeSmith à cette fin.L'avantage est que vous pouvez définir dans les propriétés du fichier de modèle à régénérer chaque build (set BuildAction = « Complile »)

Edité je aussi cherché cette solution. Après google, j'ai trouvé le template de base T4 pour générer une telle classe. Je l'ai redessiné et vous pouvez le trouver ci-dessous.

modèle génère classe wrapper pour la section appSetting de votre web.config/fichier App.config

Supposons que vous avez des lignes suivantes de paramètres dans le fichier de configuration

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

Après modèle de traitement vous obtiendrez suivant classe

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; 

    } 
} 

Enregistrer le code ci-dessous pour le fichier * .tt et comprennent yo votre projet où vous voulez mettre le fichier généré. Pour régénérer la classe sur chaque build see my answer here modèle reconnaisse la chaîne, datetime, int et types booléens à partir des valeurs

<#@ 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}""]);", [email protected], GetConverter([email protected])) 
      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 = [email protected] 
      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([email protected]), 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 

#> 
+0

Ceci est une idée intéressante, mais je ne suis pas un grand fan de CodeSmith pour être honnête. En fin de compte, j'ai écrit une de mes classes, ce qui était nécessaire dans tous les cas car il n'y avait aucun moyen pour ma demande de déduire quel type mes appsettings étaient. –

+1

J'ai ajouté un peu de code à mon message. J'espère que ça pourrait t'aider. – Cheburek

+0

C'est une solution vraiment intéressante! Une chose qu'il semblait avoir des problèmes avec (et c'est VBs IsNumeric) est "0,5,0", que VB croit être une valeur numérique, même si je ne suis pas sûr de savoir comment! –

1

Vous pouvez le faire avec une étape de pré-construction. C'est assez facile à faire - écrivez simplement un programme ou un script ou un modèle qui régénère la classe, et appelez-le dans votre événement de pré-construction - mais cela vous donnera des wigglies rouges et aucun intellisense sur les nouveaux membres jusqu'à ce que la classe régénéré.

Une approche légèrement plus manuelle, mais probablement plus pratique, serait de créer un T4 template et de l'inclure dans votre projet. Vous devrez cependant vous rappeler de re-transformer le modèle chaque fois que vous avez ajouté un nouveau paramètre. Serait-ce trop lourd?

+0

Merci pour votre réponse! De manière réaliste, je préférerais que quoi que je fasse, tout le processus de remplissage de la classe de façade est automatique et aucun travail n'est requis par le développeur. Y a-t-il des ressources préexistantes que vous connaissez et qui peuvent le faire? –

+0

Hmm, étant donné que vous êtes dans ASP.NET, vous pouvez peut-être le faire en utilisant l'espace de noms System.Web.Compilation, que je pense être ce que les projets ASP.NET Web Site utilisent pour créer leurs classes de profil. Malheureusement, ceci est en dehors de mon domaine de connaissance, et je ne suis même pas sûr que cela fonctionne dans des projets Web Application (.csproj). Pardon. Votre autre option consiste à regarder les outils personnalisés de VS (IVsSingleFileGenerator) mais ils sont conçus pour un scénario différent et ne fonctionneront que si vos définitions de paramètres sont conservées dans leur propre fichier spécial. – itowlson

+0

Ceci - http://blog.jqweb.ca/?p=44 - ressemble à quelque chose que je pourrais utiliser, mais je vais devoir m'adapter si je veux tirer de la base de données, plutôt que du web.config ... ll mettra à jour si/quand je trouve une solution. –

Questions connexes