Back to "Προσαρμοσμένες επεκτάσεις τμημάτων ρυθμίσεων"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET C#

Προσαρμοσμένες επεκτάσεις τμημάτων ρυθμίσεων

Friday, 27 September 2024

Εισαγωγή

Φαίνεται ότι ο καθένας έχει μια έκδοση αυτού του κώδικα, ήρθα για πρώτη φορά σε αυτή την προσέγγιση από Filip W και πρόσφατα ο παλιός μου συνάδελφος Phil Hack έχει την εκδοχή του.

Απλά για να ολοκληρώσω, έτσι το κάνω.

Γιατί;

Ο λόγος είναι να είναι ευκολότερο να λειτουργήσει με τη διαμόρφωση. Αυτή τη στιγμή ο πρωθυπουργός χρησιμοποιεί IOptions<T> και IOptionsSnapshot<T> Αλλά αυτό είναι ένα κομμάτι του πόνου για να λειτουργήσει με. Αυτή η προσέγγιση σας επιτρέπει να συνεργαστείτε με τη διαμόρφωση με έναν πιο φυσικό τρόπο. Οι περιορισμοί είναι ότι δεν μπορείτε να χρησιμοποιήσετε το IOptions<T> μοτίβο, ώστε να μην έχετε τη δυνατότητα να ξαναγεμίσετε τη διαμόρφωση χωρίς να επανεκκινήσετε την εφαρμογή. Ωστόσο, ειλικρινά αυτό είναι κάτι που σχεδόν ποτέ δεν θέλω να κάνω.

Ο κώδικας

Στην εκδοχή μου χρησιμοποιώ τα πρόσφατα στατικά μέλη διεπαφών για να διευκρινίσω ότι όλες οι περιπτώσεις αυτής της κατηγορίας πρέπει να δηλώσουν Section ιδιοκτησία. Αυτό χρησιμοποιείται για να πάρει το τμήμα από τη διαμόρφωση.

public interface IConfigSection {
    public static abstract string Section { get; }
}

Έτσι, για κάθε εφαρμογή θα πρέπει στη συνέχεια να προσδιορίσετε σε ποιο τμήμα αυτό να δεσμεύεται:

public class NewsletterConfig : IConfigSection
{
    public static string Section => "Newsletter";
    
    public string SchedulerServiceUrl { get; set; } = string.Empty;
    public string AppHostUrl { get; set; } = string.Empty;
}

Σε αυτή την περίπτωση ψάχνει για ένα τμήμα στη διαμόρφωση που ονομάζεται Newsletter.

  "Newsletter": {
      "SchedulerServiceUrl" : "http://localhost:5000",
        "AppHostUrl" : "https://localhost:7240"
    
  }

Θα μπορέσουμε στη συνέχεια να το συνδέσουμε αυτό με την Program.cs αρχείο όπως:


var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
services.ConfigurePOCO<NewsletterConfig>(config);

Μπορούμε επίσης να πάρουμε την αξία του config στο Program.cs αρχείο όπως:

var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(config);

Ή ακόμη και για την WebApplicationBuilder όπως έτσι:

var newsletterConfig = builder.Configure<NewsletterConfig>();

Σημείωση: Δεδομένου ότι ο κατασκευαστής έχει πρόσβαση στο ConfigurationManager Δεν χρειάζεται να το περάσει αυτό.

Που σημαίνει ότι τώρα έχεις επιλογές για το πώς να κλείσεις τη ρύθμιση.

Ένα άλλο πλεονέκτημα είναι ότι αν πρέπει να χρησιμοποιήσετε αυτές τις τιμές αργότερα στην Program.cs αρχείο τότε το αντικείμενο είναι στη διάθεσή σας.

Η μέθοδος επέκτασης

Για να μπορέσουμε όλα αυτά έχουμε μια αρκετά απλή μέθοδο επέκτασης που κάνει το έργο της δέσμευσης της διαμόρφωσης στην τάξη.

Ο παρακάτω κωδικός επιτρέπει τα ακόλουθα:

// These get the values and bind them to the class while adding these to Singleton Scope
var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(config);
var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(configuration.GetSection(NewsletterConfig.Section));
// Or for Builder...These obviously only work for ASP.NET Core applications, take them out if you are using this in a different context.
var newsletterConfig = builder.Configure<NewsletterConfig>();
var newsletterConfig = builder.Configure<NewsletterConfig>(NewsletterConfig.Section);
// These just return a dictionary, which can be useful to get all the values in a section
var newsletterConfig = builder.GetConfigSection<NewsletterConfig>();

Όλα αυτά ενεργοποιούνται από την ακόλουθη κατηγορία επέκτασης.

Μπορείτε να δείτε ότι η κύρια ώθηση αυτού είναι να χρησιμοποιήσετε τα στατικά μέλη διεπαφής για να καθορίσετε το όνομα τμήματος. Αυτό χρησιμοποιείται στη συνέχεια για να πάρει το τμήμα από τη διαμόρφωση.

namespace Mostlylucid.Shared.Config;

public static class ConfigExtensions {
    public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfigurationSection configuration)
        where TConfig : class, new() {
        if (services == null) throw new ArgumentNullException(nameof(services));
        if (configuration == null) throw new ArgumentNullException(nameof(configuration));
        
        var config = new TConfig();
        configuration.Bind(config);
        services.AddSingleton(config);
        return config;
    }

    public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, ConfigurationManager configuration)
        where TConfig : class, IConfigSection, new()
    {
        var sectionName = TConfig.Section;
        var section = configuration.GetSection(sectionName);
        return services.ConfigurePOCO<TConfig>(section);
    }
    
    public static TConfig Configure<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var services = builder.Services;
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return services.ConfigurePOCO<TConfig>(configuration.GetSection(sectionName));
    }
    

    public static TConfig GetConfig<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        var section = configuration.GetSection(sectionName).Get<TConfig>();
        return section;
        
    }
    
    public static Dictionary<string, object> GetConfigSection(this IConfiguration configuration, string sectionName) {
        var section = configuration.GetSection(sectionName);
        var result = new Dictionary<string, object>();
        foreach (var child in section.GetChildren()) {
            var key = child.Key;
            var value = child.Value;
            result.Add(key, value);
        }
        
        return result;
    }
    
    public static Dictionary<string, object> GetConfigSection<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return configuration.GetConfigSection(sectionName);
    }
}

Κατά τη χρήση

Το να τα χρησιμοποιείς είναι πολύ απλό. Σε κάθε κατηγορία όπου χρειάζεστε αυτό το config μπορείτε απλά να το κάνετε ένεση έτσι:

public class NewsletterService(NewsletterConfig config {
 
}

Συμπέρασμα

Λοιπόν αυτό είναι... αρκετά απλό αλλά είναι μια τεχνική που χρησιμοποιώ σε όλες μου τις εργασίες. Είναι ένας ωραίος τρόπος να δουλεύεις με τη διαμόρφωση και νομίζω ότι είναι λίγο πιο φυσικό από το IOptions<T> μοτίβο.

logo

©2024 Scott Galloway