किसी सेवा को कॉन्फ़िगर करते समय निर्भरता इंजेक्शन के साथ Azure फ़ंक्शन V3 में IConfiguration को इंजेक्ट या उपयोग कैसे करें


10

आम तौर पर एक .NET कोर प्रोजेक्ट में मैं DI पंजीकरण आदेशों के साथ अपनी सेवा को कॉन्फ़िगर करने के लिए एक 'बूस्टरैप' वर्ग बनाऊंगा। यह आमतौर पर एक विस्तार विधि है IServiceCollectionजहां मैं एक विधि को कॉल कर सकता हूं जैसे कि .AddCosmosDbServiceऔर आवश्यक सभी चीजें स्थिर वर्ग में 'स्व-निहित' हैं। कुंजी हालांकि इस विधि का एक हो जाता है है IConfigurationसे Startupवर्ग।

मैंने अतीत में Azure Functions में DI के साथ काम किया है, लेकिन अभी तक इस विशिष्ट आवश्यकता के पार नहीं आया हूं।

जब मैं फंक्शन अज़ुरे में तैनात किया जाता है, तो IConfigurationमैं अपने दोनों के local.settings.jsonसाथ-साथ देव / उत्पादन अनुप्रयोग सेटिंग्स से गुण मिलान वाली कंक्रीट क्लास में बाँधने के लिए उपयोग कर रहा हूँ ।

CosmosDbClientSettings.cs

/// <summary>
/// Holds configuration settings from local.settings.json or application configuration
/// </summary>    
public class CosmosDbClientSettings
{
    public string CosmosDbDatabaseName { get; set; }
    public string CosmosDbCollectionName { get; set; }
    public string CosmosDbAccount { get; set; }
    public string CosmosDbKey { get; set; }
}

BootstrapCosmosDbClient.cs

public static class BootstrapCosmosDbClient
{
    /// <summary>
    /// Adds a singleton reference for the CosmosDbService with settings obtained by injecting IConfiguration
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <returns></returns>
    public static async Task<CosmosDbService> AddCosmosDbServiceAsync(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        CosmosDbClientSettings cosmosDbClientSettings = new CosmosDbClientSettings();
        configuration.Bind(nameof(CosmosDbClientSettings), cosmosDbClientSettings);

        CosmosClientBuilder clientBuilder = new CosmosClientBuilder(cosmosDbClientSettings.CosmosDbAccount, cosmosDbClientSettings.CosmosDbKey);
        CosmosClient client = clientBuilder.WithConnectionModeDirect().Build();
        CosmosDbService cosmosDbService = new CosmosDbService(client, cosmosDbClientSettings.CosmosDbDatabaseName, cosmosDbClientSettings.CosmosDbCollectionName);
        DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(cosmosDbClientSettings.CosmosDbDatabaseName);
        await database.Database.CreateContainerIfNotExistsAsync(cosmosDbClientSettings.CosmosDbCollectionName, "/id");

        services.AddSingleton<ICosmosDbService>(cosmosDbService);

        return cosmosDbService;
    }
}

Startup.cs

public class Startup : FunctionsStartup
{

    public override async void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddHttpClient();
        await builder.Services.AddCosmosDbServiceAsync(**need IConfiguration reference**); <--where do I get IConfiguration?
    }
}

जाहिर है के लिए एक निजी क्षेत्र को जोड़ने IConfiguration में Startup.csकाम नहीं के रूप में यह कुछ के साथ आबादी होने की जरूरत है और मैं भी पढ़ा है कि होगा के लिए डि का उपयोग कर IConfigurationएक अच्छा विचार नहीं है

मैंने भी वर्णित के रूप में विकल्प पैटर्न का उपयोग करने की कोशिश की है यहाँ है और इस तरह लागू किया गया है:

builder.Services.AddOptions<CosmosDbClientSettings>()
    .Configure<IConfiguration>((settings, configuration) => configuration.Bind(settings));

हालांकि यह IOptions<CosmosDbClientSettings>एक गैर-स्थिर वर्ग को इंजेक्ट करने के लिए काम करेगा , मैं अपने कॉन्फ़िगरेशन कार्य को होल्ड करने के लिए एक स्थिर वर्ग का उपयोग कर रहा हूं।

मैं इस काम या संभावित वर्कअराउंड कैसे बना सकता हूं, इस पर कोई सुझाव? मैं सभी कॉन्फ़िगरेशन को एक स्थान पर रखना चाहता हूं (बूटस्ट्रैप फ़ाइल)।

जवाबों:


5

लिंक किए गए उदाहरण खराब डिज़ाइन किया गया है (मेरी राय में)। यह चुस्त युग्मन और एसिक्स-वेट और ब्लॉकिंग कॉल्स के मिश्रण को प्रोत्साहित करता है।

IConfigurationडिफ़ॉल्ट रूप से स्टार्ट अप के हिस्से के रूप में सेवा संग्रह में जोड़ा जाता है, इसलिए मैं निर्भरता के आस्थगित समाधान का लाभ लेने के लिए आपके डिज़ाइन को बदलने का सुझाव दूंगा ताकि एक कारखाने के प्रतिनिधि का उपयोग करके IConfigurationनिर्मित के माध्यम से हल किया जा सके IServiceProvider

public static class BootstrapCosmosDbClient {

    private static event EventHandler initializeDatabase = delegate { };

    public static IServiceCollection AddCosmosDbService(this IServiceCollection services) {

        Func<IServiceProvider, ICosmosDbService> factory = (sp) => {
            //resolve configuration
            IConfiguration configuration = sp.GetService<IConfiguration>();
            //and get the configured settings (Microsoft.Extensions.Configuration.Binder.dll)
            CosmosDbClientSettings cosmosDbClientSettings = configuration.Get<CosmosDbClientSettings>();
            string databaseName = cosmosDbClientSettings.CosmosDbDatabaseName;
            string containerName = cosmosDbClientSettings.CosmosDbCollectionName;
            string account = cosmosDbClientSettings.CosmosDbAccount;
            string key = cosmosDbClientSettings.CosmosDbKey;

            CosmosClientBuilder clientBuilder = new CosmosClientBuilder(account, key);
            CosmosClient client = clientBuilder.WithConnectionModeDirect().Build();
            CosmosDbService cosmosDbService = new CosmosDbService(client, databaseName, containerName);

            //async event handler
            EventHandler handler = null;
            handler = async (sender, args) => {
                initializeDatabase -= handler; //unsubscribe
                DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
                await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
            };
            initializeDatabase += handler; //subscribe
            initializeDatabase(null, EventArgs.Empty); //raise the event to initialize db

            return cosmosDbService;
        };
        services.AddSingleton<ICosmosDbService>(factory);
        return service;
    }
}

async voidगैर-एसिंक्स ईवेंट हैंडलर में उपयोग करने के लिए प्राप्त होने वाले दृष्टिकोण पर ध्यान दें ।

संदर्भ Async / Await - अतुल्यकालिक प्रोग्रामिंग में सर्वश्रेष्ठ अभ्यास

तो अब Configureठीक से मंगवाया जा सकता है।

public class Startup : FunctionsStartup {

    public override void Configure(IFunctionsHostBuilder builder) =>
        builder.Services
            .AddHttpClient()
            .AddCosmosDbService();
}

5

यहाँ एक उदाहरण है कि मैं कोड़ा मारने में सक्षम था; यह केंद्रीयकृत कॉन्फ़िगरेशन और सुविधा प्रबंधन के लिए एज़्योर ऐप कॉन्फ़िगरेशन के लिए एक कनेक्शन स्थापित करता है । एक को सभी DI सुविधाओं का उपयोग करने में सक्षम होना चाहिए, जैसे IConfigurationऔरIOptions<T> ASP वे ASP.NET कोर नियंत्रक में होते हैं।

NuGet निर्भरताएँ

  • Install-Package Microsoft.Azure.Functions.Extensions
  • Install-Package Microsoft.Extensions.Configuration.AzureAppConfiguration

Startup.cs

[assembly: FunctionsStartup(typeof(Startup))]

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder hostBuilder) {
        var serviceProvider = hostBuilder.Services.BuildServiceProvider();
        var configurationRoot = serviceProvider.GetService<IConfiguration>();
        var configurationBuilder = new ConfigurationBuilder();
        var appConfigEndpoint = configuration["AppConfigEndpoint"];

        if (configurationRoot is IConfigurationRoot) {
            configurationBuilder.AddConfiguration(configurationRoot);
        }

        if (!string.IsNullOrEmpty(appConfigEndpoint)) {
            configurationBuilder.AddAzureAppConfiguration(appConfigOptions => {
                // possible to run this locally if refactored to use ClientSecretCredential or DefaultAzureCredential
                appConfigOptions.Connect(new Uri(appConfigEndpoint), new ManagedIdentityCredential());
            });
        }

        var configuration = configurationBuilder.Build();

        hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

        // Do more stuff with Configuration here...
    }
}

public sealed class HelloFunction
{
    private IConfiguration Configuration { get; }

    public HelloFunction(IConfiguration configuration) {
        Configuration = configuration;
    }

    [FunctionName("HelloFunction")]
    public void Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log) {
        log.LogInformation($"Timer Trigger Fired: 'Hello {Configuration["Message"]}!'");
    }
}

इस दृष्टिकोण के साथ, मेरे पास एक मुद्दा है कि host.jsonमापदंडों का उपयोग नहीं किया जाता है, विशेष रूप से,routePrefix
एंड्री

1
@Andrii दिलचस्प, मुझे कुछ शोध करना होगा और अगर कोई समाधान मिलता है तो मैं अपनी पोस्ट संपादित करूंगा; सिर के लिए एक टन धन्यवाद!
किट्सो ०२४४
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.