自托管.NET Core控制台应用程序中的Startup.cs

Dan*_*iel 44 c# console-application .net-core

我有一个自托管的.NET Core Console应用程序.

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();
}
Run Code Online (Sandbox Code Playgroud)

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

怎么样?

Dan*_*iel 63

所以我遇到了这个解决方案,受到了接受的答案的启发:

Program.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Startup.cs

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

appsettings.json

{
    "SomeConfigItem": {
        "Token": "8201342s223u2uj328",
        "BaseUrl": "http://localhost:5000"
    }
}
Run Code Online (Sandbox Code Playgroud)

MyService.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

IMyService.cs

public interface IMyService
{
    Task MyServiceMethod();
}
Run Code Online (Sandbox Code Playgroud)

  • 这非常有帮助。如果您的应用程序使用 ASP.NET Core MVC,它甚至可以工作(只要您的控制器是精简的并且您的视图中没有应用程序逻辑)。我必须做的另一件事是创建我自己的 Microsoft.AspNetCore.Hosting.Internal.HostingEnvironment 子类 MyHostingEnvironment 并设置其 ContentRootPath = Path.GetFullPath("."); 然后在 Program.cs 中调用 new Startup 之前执行 IHostingEnvironment env = new MyHostingEnvironment(); services.AddSingleton&lt;IHostingEnvironment, MyHostingEnvironment&gt;(); 然后将“new Startup()”更改为“new Startup(env)”。 (2认同)
  • 也许暴露 IConfiguration 而不是 IConfigurationRoot 更好,它具有更好的扩展支持 (2认同)

And*_*ndy 45

此答案基于以下标准:

我想CreateDefaultBuilder在一个简单的控制台应用程序中使用没有任何 ASP.NET web 内容的新通用主机,但也能够松散启动逻辑startup.cs以便配置AppConfiguration和服务

所以我花了一个上午的时间来研究如何做这样的事情。这是我想出来的……

此方法需要的唯一 nuget 包是Microsoft.Extensions.Hosting(在撰写本文时它是 version 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!
}
Run Code Online (Sandbox Code Playgroud)

你添加一个Startup.cs应该是这样的:

public class Startup
{
    public IConfiguration Configuration { get; }

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

    public void ConfigureServices(IServiceCollection services)
    {
        // Configure your services here
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像在典型的 ASP.NET Core 应用程序中一样配置服务(无需安装 ASP.NET Core Web Hosting)。

演示项目

我整理了一个 .NET Core 3.1 控制台演示项目,用于IHostedService执行各种操作,例如实现、BackgroundService实现、瞬态/单例服务。我也注射了IHttpClientFactoryIMemoryCache采取了很好的措施。

克隆该 repo 并试一试。

这个怎么运作

我创建了一个IHostBuilder扩展方法,它只是实现了IHostBuilder UseStartup<TStartup>(this IHostBuilder hostBuilder)我们都习惯的模式。

由于CreateDefaultBuilder() 添加了所有基础知识,因此没有什么可添加的了。我们唯一关心的是IConfiguration通过ConfigureServices(IServiceCollection).

扩展方法源代码

/// <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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 现在我们需要一个带有 `HostBuilderExtensions` 的 PR 到核心! (5认同)
  • @KyleMit——太棒了!这玩起来很有趣。我*不*知道您可以在没有 WebHost 的情况下运行“IHost”。这对于未来的项目将会派上用场。感谢您发布问题。 (4认同)
  • @user1034912——你能解释一下吗?作为一名开发人员,您会认为您应该比“它不再工作了”更明确——这通常是*客户*所说的:) (4认同)
  • @KyleMit - 添加了功能请求:https://github.com/dotnet/extensions/issues/3489 (3认同)
  • - 哦 - 我喜欢这个! (2认同)
  • 肯定会给你赏金 - 但会留下更多的眼球,如果有更多值得赏金的好答案,我会打开另一个 (2认同)
  • @JeremyLakeman——我对此进行了调查。它是“Microsoft.AspNetCore.Hosting”包的一部分,这是他想要避免的。我考虑过重新实现该部分,但最终它是该包中的大量“复制和粘贴”代码,因此我停止了沿着这条路走下去。 (2认同)
  • @KyleMit——非常感谢您的赏金。你让我度过了一个美好的夜晚——我真的很感激! (2认同)
  • 感谢您的一个回答!每当你想用针对核心的 PR 更新你的答案时,再做 500 次代表:) (2认同)
  • 嘿@KyleMit——哇,我没有注意到你对这个答案给予了更多的关注。感谢那!我沿着这条路去研究“IWebHostBuilder”是如何做到这一点的,然后我开始重复使用很多代码,所以我停了下来。但是,如果我们想遵循该模式,那么我们可能应该使其相似(例如,创建一个“IStartup”接口)。我会解决这个问题,然后添加到我们的功能请求中。抱歉我没有早点回复。完全不知道你评论了。 (2认同)
  • @KyleMit - 有趣的是他们基本上在做与我们相同的事情:https://github.com/dotnet/aspnetcore/blob/cfd20ad2d71ff63dc9a06be8bf8628321e5bac99/src/Hosting/Hosting/src/Internal/StartupLoader.cs 我们刚刚做到了以一种非常简单的方式(仅涵盖一个用例,而他们的用例涵盖了许多用例)我不再相信我们是使用反射的“擦鞋”模式,因为他们做了同样的事情。我从功能请求中删除了这个想法。 (2认同)
  • @安迪,谢谢!没有压力——如果你有时间/有兴趣就戳它。代表是你的,你已经赢得了 (2认同)
  • @Andy那么我们实际上在哪里运行控制台应用程序的代码?我一定在这里错过了一些简单的东西...... (2认同)

Ara*_*edi 23

所有.NET Core应用程序都由精心设计的独立库和包组成,您可以在任何类型的应用程序中自由引用和使用它们.事实上,Asp.net core应用程序预先配置为引用许多这些库并公开http端点.

但是,如果您的控制台应用程序需要依赖注入,只需引用相应的库即可.这是一个指南:http://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/


Ped*_*lho 9

我知道这个线程有点旧,但我还是决定分享我的代码,因为它也实现了 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
        }
    }
Run Code Online (Sandbox Code Playgroud)


Raj*_*Raj 6

另一种方法是使用HostBuilderfrom Microsoft.Extensions.Hosting包。

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();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我遇到了同样的问题,我认为这是一个很好的解决方案:

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))
}
Run Code Online (Sandbox Code Playgroud)