2009-10-18 14 views
5

oto pytanie pierwsze:Automatyczne generowanie silnie wpisany AppSettings klasę

Czy to możliwe? Inspirację czerpię z Joe Wrobel's work (redux zapomnianego Codeplex project). Tutaj wykonujesz swoją pracę nad stworzeniem swojego profilu dla dostawcy, a także zajmuje się tworzeniem silnego pisania dla niego, skutecznie tworząc elewację dla klasy Profile.

A teraz historia z tyłu!

Naprawdę nie podoba mi się magic strings. Są dość złe i mogą powodować poważne problemy przy aktualizacji aplikacji. Pracując w językach takich jak PHP i ColdFusion, wiem, że łatwo jest umieścić je w swojej aplikacji i zapomnieć o nich, dopóki nie trzeba ich zmienić. A potem musisz polować na każdą odmianę i odpowiednio je zmienić.

.NET nie jest o wiele lepszy, jeśli zastosujesz szablony aplikacji "od razu po wyjęciu z pudełka". Wiele przykładów tam dostępnych używa appsettings w web.config do przechowywania różnych ustawień. Jest to rzeczywiście dobre miejsce do przechowywania i jest idealne dla większości aplikacji. Problemy zaczynają się jednak pojawiać, gdy zaczniesz je wywoływać bezpośrednio - na przykład ConfigurationManager.AppSettings["MyAppSetting"]. W takim razie nie jesteś wcale lepszy od użytkownika PHP, ponieważ używasz magicznych ciągów.

W tym miejscu przychodzi facades. Elewacje oferują sposób tworzenia silnie wpisanego obiektu z magicznego łańcucha w jednym miejscu i odsyłając go do dewelopera od reszty aplikacji.

Teraz, zamiast używać pliku web.config do przechowywania moich ustawień, używam bazy danych do przechowywania wszystkich. Po uruchomieniu aplikacji, combo nazwa/wartość są pobierane, a następnie są kolejno dodawane do ConfigurationManager.AppSettings przez Set. Bez biggie (poza problem miałem wcześniej!).

Ta "elewacja aplikacji" jest dostępna dla mojej warstwy danych, warstwy usługi i warstwy prezentacji i zawiera rzeczy takie jak tryb aplikacji, który punkt końcowy usługi używa yada yada yada i ogranicza potrzebę polowania na wiele ciągów magicznych, do dwóch magicznych łańcuchów - jednego (nazwa) w elewacji, a drugiego (nazwa i wartość) w punkcie tworzenia (który, dla mnie jest db).

Ta klasa elewacji nareszcie się urośnie i ostatecznie zmęczy mnie konieczność ich aktualizacji.

Tak więc chciałbym mieć klasę ApplicationFacade, która generuje się automatycznie za każdym razem, gdy kompilacja jest wykonywana. A teraz z powrotem na początek ... Czy to możliwe?

Odpowiedz

7

Można również użyć do tego celu szablonów CodeSmith.Zaletą jest to, że można ustawić we właściwościach pliku szablonu do regeneracji po każdej kompilacji (set BuildAction = „Complile”)

Zmieniano Spojrzałem też na takie rozwiązanie. Po uruchomieniu go znalazłem podstawowy szablon T4 do wygenerowania takiej klasy. Przeprojektowałem go i możesz go znaleźć poniżej.

Szablon generuje klasy otoki dla sekcji AppSetting z Web.config/pliku app.config

Załóżmy, że następujące linie ustawień w pliku konfiguracyjnym

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

Po przetworzeniu szablonu będzie się następujący Klasa

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; 

    } 
} 

Zapisz następujący kod do pliku * .tt i należą do yo ur projekt, w którym chcesz umieścić wygenerowany plik. Aby zregenerować klasę na każdym budować see my answer here Szablon rozpoznawać typy ciągów, datetime, int i bool od wartości

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

To interesujący pomysł, ale nie jestem wielkim fanem CodeSmith, szczerze mówiąc. W końcu napisałem własną klasę, która była wymagana w każdym przypadku, ponieważ nie było sposobu, aby moja aplikacja mogła wywnioskować, jaki typ były moje ustawienia. –

+1

Dodałem trochę kodu do mojego postu. Mam nadzieję, że to ci pomoże. – Cheburek

+0

To naprawdę ciekawe rozwiązanie! Jedną z rzeczy, z którymi wydawało się mieć problemy (a jest to VBs IsNumeric) jest "0,5,0", co według VB jest wartością numeryczną, chociaż nie jestem pewien jak! –

1

Można to zrobić, wykonując etap wstępnej kompilacji. Jest to dość łatwe do zrobienia - wystarczy napisać program lub skrypt lub szablon, który regeneruje klasę i wywołać ją w swoim wstępnym wydarzeniu - ale to da ci czerwone wigglies i brak intellisense na nowych członkach dopóki klasa nie otrzyma zregenerowany.

Nieco bardziej ręczne, ale prawdopodobnie bardziej wygodne podejście polegałoby na stworzeniu T4 template i uwzględnienia go w projekcie. Musisz jednak pamiętać, aby ponownie przekształcić szablon za każdym razem, gdy dodałeś nowe ustawienie. Czy byłoby to zbyt uciążliwe?

+0

Dziękuję za odpowiedź! Realistycznie wolałbym, aby cokolwiek robiłem, cały proces wypełniania klasy elewacji był automatyczny i deweloper nie wymaga żadnej pracy. Czy istnieją jakieś istniejące wcześniej zasoby, które mogą to zrobić? –

+0

Hmm, ponieważ jesteś w ASP.NET, możesz być w stanie to zrobić, używając przestrzeni nazw System.Web.Compilation, co moim zdaniem jest tym, czego projekty ASP.NET Web Site używają do tworzenia swoich klas profili. Niestety, jest to poza moim obszarem wiedzy i nie jestem nawet pewien, czy działa w projektach aplikacji sieci Web (opartych na .csproj). Przepraszam. Inną opcją jest przeglądanie niestandardowych narzędzi VS (IVsSingleFileGenerator), ale są one przeznaczone do innego scenariusza i będą działać tylko wtedy, gdy definicje ustawień będą przechowywane w ich własnym pliku specjalnym. – itowlson

+0

To - http://blog.jqweb.ca/?p=44 - wygląda jak coś, czego mógłbym użyć, ale muszę się dostosować, jeśli chcę pobrać z bazy danych, a nie z pliku web.config ... I ' Aktualizuję, jeśli/kiedy znajdę rozwiązanie. –

Powiązane problemy