簡體   English   中英

自托管 .NET 核心控制台應用程序中的 Startup.cs

[英]Startup.cs in a self-hosted .NET Core Console Application

我有一個自托管的.NET 核心控制台應用程序

web 顯示了ASP.NET Core的示例,但我沒有 web 服務器。 只是一個簡單的命令行應用程序。

是否可以為控制台應用程序做這樣的事情?

public static void Main(string[] args)
{
    // I don't want a WebHostBuilder. Just a command line

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

我想在 ASP.NET Core 中使用 Startup.cs,但在控制台上。

我該怎么辦?

所以我遇到了這個解決方案,靈感來自公認的答案:

程序.cs

public class Program
{
    public static void Main(string[] args)
    {
        IServiceCollection services = new ServiceCollection();
        // Startup.cs finally :)
        Startup startup = new Startup();
        startup.ConfigureServices(services);
        IServiceProvider serviceProvider = services.BuildServiceProvider();

        //configure console logging
        serviceProvider
            .GetService<ILoggerFactory>()
            .AddConsole(LogLevel.Debug);

        var logger = serviceProvider.GetService<ILoggerFactory>()
            .CreateLogger<Program>();

        logger.LogDebug("Logger is working!");

        // Get Service and call method
        var service = serviceProvider.GetService<IMyService>();
        service.MyServiceMethod();
    }
}

啟動文件

public class Startup
{
    IConfigurationRoot Configuration { get; }

    public Startup()
    {
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json");

        Configuration = builder.Build();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLogging();
        services.AddSingleton<IConfigurationRoot>(Configuration);
        services.AddSingleton<IMyService, MyService>();
    }
}

appsettings.json

{
    "SomeConfigItem": {
        "Token": "8201342s223u2uj328",
        "BaseUrl": "http://localhost:5000"
    }
}

我的服務.cs

public class MyService : IMyService
{
    private readonly string _baseUrl;
    private readonly string _token;
    private readonly ILogger<MyService> _logger;

    public MyService(ILoggerFactory loggerFactory, IConfigurationRoot config)
    {
        var baseUrl = config["SomeConfigItem:BaseUrl"];
        var token = config["SomeConfigItem:Token"];

        _baseUrl = baseUrl;
        _token = token;
        _logger = loggerFactory.CreateLogger<MyService>();
    }

    public async Task MyServiceMethod()
    {
        _logger.LogDebug(_baseUrl);
        _logger.LogDebug(_token);
    }
}

IMyService.cs

public interface IMyService
{
    Task MyServiceMethod();
}

此答案基於以下標准:

我想在一個簡單的控制台應用程序中使用沒有任何 ASP.NET web 內容的新通用主機CreateDefaultBuilder ,但也能夠在startup.csAppConfiguration啟動邏輯以配置AppConfiguration和服務

所以我花了一個上午的時間來研究如何做這樣的事情。 這是我想出來的……

此方法需要的唯一 nuget 包是Microsoft.Extensions.Hosting (在撰寫本文時,它的版本為3.1.7 )。 這是nuget 包的鏈接。 這個包也需要使用CreateDefaultBuilder() ,所以你很可能已經添加了它。

將擴展(答案底部的擴展代碼)添加到項目后,將程序條目設置為類似於以下內容:

using Microsoft.Extensions.Hosting;

class Program
{
    static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
        await host.RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseStartup<Startup>(); // our new method!
}

您添加一個Startup.cs ,它應該如下所示:

public class Startup
{
    public IConfiguration Configuration { get; }

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

    public void ConfigureServices(IServiceCollection services)
    {
        // Configure your services here
    }
}

然后,您可以像在典型的 ASP.NET Core 應用程序中一樣配置您的服務(無需安裝 ASP.NET Core Web Hosting)。

演示項目

我整理了一個 .NET Core 3.1 控制台演示項目,用於執行各種操作,例如IHostedService實現、 BackgroundService實現、瞬態/單例服務。 我還注入了IHttpClientFactoryIMemoryCache以進行很好的衡量。

克隆該 repo 並試一試。

這個怎么運作

我創建了一個IHostBuilder擴展方法,它只是實現了我們都習慣的IHostBuilder UseStartup<TStartup>(this IHostBuilder hostBuilder)模式。

由於CreateDefaultBuilder() 添加了所有基礎知識,因此沒有太多可添加的內容。 我們唯一關心的是通過ConfigureServices(IServiceCollection)獲取IConfiguration並創建我們的服務管道。

擴展方法源代碼

/// <summary>
/// Extensions to emulate a typical "Startup.cs" pattern for <see cref="IHostBuilder"/>
/// </summary>
public static class HostBuilderExtensions
{
    private const string ConfigureServicesMethodName = "ConfigureServices";

    /// <summary>
    /// Specify the startup type to be used by the host.
    /// </summary>
    /// <typeparam name="TStartup">The type containing an optional constructor with
    /// an <see cref="IConfiguration"/> parameter. The implementation should contain a public
    /// method named ConfigureServices with <see cref="IServiceCollection"/> parameter.</typeparam>
    /// <param name="hostBuilder">The <see cref="IHostBuilder"/> to initialize with TStartup.</param>
    /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
    public static IHostBuilder UseStartup<TStartup>(
        this IHostBuilder hostBuilder) where TStartup : class
    {
        // Invoke the ConfigureServices method on IHostBuilder...
        hostBuilder.ConfigureServices((ctx, serviceCollection) =>
        {
            // Find a method that has this signature: ConfigureServices(IServiceCollection)
            var cfgServicesMethod = typeof(TStartup).GetMethod(
                ConfigureServicesMethodName, new Type[] { typeof(IServiceCollection) });

            // Check if TStartup has a ctor that takes a IConfiguration parameter
            var hasConfigCtor = typeof(TStartup).GetConstructor(
                new Type[] { typeof(IConfiguration) }) != null;

            // create a TStartup instance based on ctor
            var startUpObj = hasConfigCtor ?
                (TStartup)Activator.CreateInstance(typeof(TStartup), ctx.Configuration) :
                (TStartup)Activator.CreateInstance(typeof(TStartup), null);

            // finally, call the ConfigureServices implemented by the TStartup object
            cfgServicesMethod?.Invoke(startUpObj, new object[] { serviceCollection });
        });

        // chain the response
        return hostBuilder;
    }
}

所有.NET Core應用程序都由精心設計的獨立庫和包組成,您可以在任何類型的應用程序中自由引用和使用它們。 碰巧的是, Asp.net core應用程序預先配置為引用許多這些庫並公開一個 http 端點。

但是,如果您的控制台應用程序需要依賴注入,只需引用相應的庫即可。 這是一個指南: https : //andrewlock.net/using-dependency-injection-in-a-net-core-console-application/

另一種方法是使用Microsoft.Extensions.Hosting包中的HostBuilder

public static async Task Main(string[] args)
{
    var builder = new HostBuilder()
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.SetBasePath(Directory.GetCurrentDirectory());
            config.AddJsonFile("appsettings.json", true);
            if (args != null) config.AddCommandLine(args);
        })
        .ConfigureServices((hostingContext, services) =>
        {
            services.AddHostedService<MyHostedService>();
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddConfiguration(hostingContext.Configuration);
            logging.AddConsole();
        });

    await builder.RunConsoleAsync();
}

我知道這個線程有點舊,但我還是決定分享我的代碼,因為它也實現了 Daniel 想要的(控制台應用程序中的 DI),但沒有 Startup 類。 Ps.:請注意,此解決方案適用於 .NET Core 或 .NET Framework。

程序.cs:

public class Program
    {

        public static void Main(string[] args)
        {
            var services = new ServiceCollection();

            DependencyInjectionConfiguration.ConfigureDI(services);

            var serviceProvider = services.BuildServiceProvider();

            var receiver = serviceProvider.GetService<MyServiceInterface>();

            receiver.YourServiceMethod();
        }
    }

public static class DependencyInjectionConfiguration
    {
        public static void ConfigureDI(IServiceCollection services)
        {
            services.AddScoped<MyServiceInterface, MyService>();
            services.AddHttpClient<MyClient>(); // for example
        }
    }

我遇到了同樣的問題,我認為這是一個很好的解決方案:

class Program
{
    static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
        await host.RunAsync();
    }

     public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostBuilderContext, serviceCollection) => new Startup(hostBuilderContext.Configuration).ConfigureServices(serviceCollection))
}

是的。 ASP.NET Core 應用程序可以是自托管的(如您的示例中所示),也可以托管在 Web 服務器(如 IIS)中。 在 .NET Core 中,所有應用程序都是控制台應用程序。

您可以更輕松地解決它。 只需制作 2 個文件,然后編輯 Main 方法:

啟動文件

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
(...)
 public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();


            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action}");
            });
        }
    }

YourService.cs

using Microsoft.AspNetCore.Mvc;
(...)
public class MyeasynetworkController : Controller
    {
        [HttpGet]
        public IActionResult Run()
        {

            return Ok("Im Working");
        }
    }

並編輯您的靜態 void Main(string[] args)

            var host = new WebHostBuilder()
                .UseKestrel()
                .UseUrls("http://+:5000")
                .UseStartup<Startup>()
                .Build();

            host.Run();

然后打開http://localhost:5000/Myeasynetwork/Run ,你應該看到 http status 200 響應,純文本我正在工作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM