2010-12-06 27 views
4

Ich habe ein Problem, bei dem ich eine Einstellungsstruktur für meine App erstellen möchte, die hinsichtlich Lokalisierung, Erweiterung und Gruppierung so optimal wie möglich sein muss. Ich möchte die Einstellungen je Entitätstyp gruppieren (Sie können sich das als Gruppierungseinstellungen pro Controller vorstellen). Die Einstellungen werden dem Benutzer angezeigt, so dass für jede Einstellung ein hübscher Titel und eine Beschreibung benötigt werden, die beide lokalisiert werden müssen. Neue Einstellungen werden nur von den Entwicklern eingeführt und eine Neukompilierung wird benötigt.Entwerfen einer Einstellungsstruktur

Was ich entwickelt habe, ist eine Klasse, die Einstellungen als statische Eigenschaften verfügbar macht, so dass sie leicht zugänglich sind, um in der gesamten Anwendung in einer statischen Weise verwendet werden. Die Einstellungen werden geladen, wenn die Klasse das erste Mal erstellt wird (was passiert, wenn die Einstellung angefordert wird), und ich verwende die Datenbank, um die Einstellungen zu speichern und sie mithilfe der Reflektion zur Laufzeit ihren entsprechenden Eigenschaften zuzuordnen.

Es sieht aus wie dieses

public class FirmSettings 
{ 
    private static IFirmSettingsRepository _repository { get; set; } 

    public static bool ShowInvoicePaymentDetails { get; set; } 

    public static bool ShowInvoiceDiscountValue { get; set; } 

    public static bool ShowDocumentComment { get; set; } 

    public static bool ShowDocumentTaxStatement { get; set; } 

    public static bool ShowDocumentAuthor { get; set; } 

    #region Constructors 

    /// <summary> 
    ///  Initializes a new instance of the <see cref = "FirmSettings" /> class. 
    /// </summary> 
    static FirmSettings() 
    { 
     Load(); 
    } 

    #endregion 

    #region Load Settings 

    public static void Load() 
    { 
     _repository = MvcApplication.Container.Get<IFirmSettingsRepository>(); 
     Type settingsType = typeof (FirmSettings); 

     //------------------------------------------------------------ 
     // Enumerate through individual settings nodes 
     //------------------------------------------------------------ 
     StringDictionary dic = _repository.LoadSettings(); 

     if (dic == null) 
     { 
      Save(); // prepares the settings with blank settings 
      dic = _repository.LoadSettings(); // reload 
     } 

     foreach (string key in dic.Keys) 
     { 
      //------------------------------------------------------------ 
      // Extract the setting's name/value pair 
      //------------------------------------------------------------ 
      string name = key; 
      string value = dic[key]; 

      //------------------------------------------------------------ 
      // Enumerate through public properties of this instance 
      //------------------------------------------------------------ 
      foreach (PropertyInfo propertyInformation in settingsType.GetProperties(BindingFlags.Public | 
                        BindingFlags.Static)) 
      { 
       //------------------------------------------------------------ 
       // Determine if configured setting matches current setting based on name 
       //------------------------------------------------------------ 
       if (propertyInformation.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) 
       { 
        //------------------------------------------------------------ 
        // Attempt to apply configured setting 
        //------------------------------------------------------------ 
        try 
        { 
         if (propertyInformation.CanWrite) 
         { 
          propertyInformation.SetValue(typeof (FirmSettings), 
                 Convert.ChangeType(value, propertyInformation.PropertyType, 
                      CultureInfo.CurrentCulture), null); 
         } 
        } 
        catch 
        { 
         // TODO: Log exception to a common logging framework? 
        } 
        break; 
       } 
      } 
     } 

     // perform resave if there are any new settings 
     Save(); 
    } 

    #endregion 

    #region Save settings 

    /// <summary> 
    ///  Saves the settings to disk. 
    /// </summary> 
    public static void Save() 
    { 
     StringDictionary dic = new StringDictionary(); 
     Type settingsType = typeof (FirmSettings); 

     //------------------------------------------------------------ 
     // Enumerate through settings properties 
     //------------------------------------------------------------ 
     foreach (PropertyInfo propertyInformation in settingsType.GetProperties(BindingFlags.Public | 
                       BindingFlags.Static)) 
     { 
      //------------------------------------------------------------ 
      // Extract property value and its string representation 
      //------------------------------------------------------------ 
      object propertyValue = propertyInformation.GetValue(typeof (FirmSettings), null); 

      string valueAsString; 
      //------------------------------------------------------------ 
      // Format null/default property values as empty strings 
      //------------------------------------------------------------ 
      if (propertyValue == null || propertyValue.Equals(Int32.MinValue) || propertyValue.Equals(Single.MinValue)) 
      { 
       valueAsString = String.Empty; 
      } 
      else 
      { 
       valueAsString = propertyValue.ToString(); 
      } 
      //------------------------------------------------------------ 
      // Write property name/value pair 
      //------------------------------------------------------------ 
      dic.Add(propertyInformation.Name, valueAsString); 
     } 

     _repository.SaveSettings(dic); 
    } 

    #endregion 
} 

Jede Einstellung in DB als Kleinbuchstaben Version des Eigenschaftsnamen gespeichert (zum Laden außer Acht lassen wir den Fall). Dasselbe gilt für den Lokalisierungsstring, der beispielsweise als FirmSettings_ShowDocumentTaxStatement_Title und FirmSettings_ShowDocumentTaxStatement_Desc gespeichert wird. (Konvention)

Dieser Ansatz löst jedoch keine Gruppierung. In der Benutzeroberfläche ist eine Gruppierung von Einstellungen erforderlich, sodass die Rechnungseinstellungen in einer Gruppe angezeigt werden. Ich könnte ein Präfix für bestimmte Einstellungen einführen und dann basierend auf dem Präfix rendern (eine andere Konvention).

Mögen Sie diesen Ansatz? Wenn nicht, wie machst du das? Es gibt viele Konventionen in diesem Ansatz und das stört mich nur ein wenig.

Antwort

1

Sie verloren mich hier ...

Ich sehe Sie einen Container von einer Art verwenden, also warum jedes Mal, wenn Referenz Sie nicht nur eine einzelne Instanz dieser Einstellungen Klasse injizieren, um es brauchen? Statische Klasse + Methoden sind schlecht für Komponententests (die Sie hierfür benötigen).

Außerdem verstehe ich nicht, warum Sie Reflexionen/String-Matching zum Einstellen von Speicher/Retrieval verwenden möchten. Wenn Sie wirklich viele Einstellungen mit komplexen Gruppierungen zwischen ihnen haben, müssen Sie die Zeit investieren, um eine richtige DAL zu finden.

Nur eine Anmerkung, Ihr "Schlüssel" (z. B. FirmSettings_ShowDocumentTaxStatement_Title) enthält keinen Namespace, wenn also zwei Klassen den gleichen Namen und die gleiche Methode haben, werden Sie mit einem Fehler konfrontiert, der schwer zu fangen ist. Das ist nur ein einfaches Szenario. Mein Punkt ist, eine Zeichenkette, die Ihrem Klassen- + Methodennamen zum Zweck der Identifikation entspricht, ist keine gute Idee. (Weil ich annehme, dass Sie ein großes kompliziertes Projekt haben, um auf diese Art Einstellungsmanagement zurückzugreifen.)

Schließlich, "ich weiß nicht, wie sonst würden Sie statischen Eigenschaften (oder regelmäßigen Eigenschaften) zur Laufzeit anderen einen Wert zuweisen als mit der Verwendung von Reflexion. " Sie können Klassen-/Methoden-/Eigenschaftenattribute verwenden und eine Factory-Klasse (in Ihrem Fall) einen Singleton Ihrer gewünschten Einstellungsklasse ausgeben lassen.Die entsprechenden DB-Spalten-/Zeilenzuordnungsinformationen können in den Attributen enthalten sein.

P.S. Reflexion ist in Bezug auf die Leistung in Ordnung. Verwenden Sie keine statische Klasse, verwenden Sie stattdessen Singleton und führen Sie eine Hintergrundaufgabe bei der Initialisierung der Einstellungsklassen aus. Sobald das Singleton initiiert wurde, müssen Sie es nicht erneut initialisieren. Aber was auch immer Sie tun, ich rate Ihnen dringend, die Zeichenfolge mit Klassen-/Methoden-Namen zu verlieren.

P.P.S. Einblick in AoP/Richtlinien-Injektion (oder war es Intervention in Microsoft Unity DI-Container). Ich glaube denen die vielleicht hilfreich sind?

P.P.S. Schließlich in 3 Post-Skript ist schlecht Englisch ...

+0

K TBH, ich bin mir nicht wirklich sicher, ob ich die Schreibfrage beantworte ... –

+0

Vielen Dank für Ihre Meinung zu Singleton, es lohnt sich, es auszuprobieren und zu versuchen, diese Route zu gehen. Ich habe eine neue Frage über das Problem des Spiels mit Singleton oder statischen Klassen geöffnet http://stackoverflow.com/questions/4382537/singleton-class-or-a-class-with-static-methods-in-asp-net-mvc- Anwendung – mare

0

IMHO ist die Lösung zu kompliziert. Versuchen Sie sich etwas Einfacheres einfallen zu lassen. Erstens verwende ich keine Reflektion (Performance-Gründe) und zweitens würde ich Konfigurationsdateien (XML) anstelle von DBs für Anwendungs-/Benutzereinstellungen verwenden.

Siehe this umfassender Artikel über Reflexion und Leistung.

+1

Ich kann nicht die Verwendung von Reflexion zu sehen, die Leistungsprobleme hier –

+0

@Kev Im Allgemeinen Reflexion ist langsamer und ist zu vermeiden. Das Speichern von Einstellungen in meiner Wahl ist nicht etwas, das eine Reflexion erfordert. Es kann auf viele verschiedene andere Arten geschehen. –

+0

Es wurde bereits entschieden, DB zu verwenden, so dass hier keine Änderungen vorgenommen werden. Wir wollen alles in DB wegen der einfachen Wartung. – mare