简体   繁体   English

.NET DI 内部构造函数和控制台日志记录

[英].NET DI Internal Constructor and Console Logging

I have the following classes in ProjectA :我在ProjectA中有以下类:

public class ApplicationBuilder
{
    private readonly IServiceCollection _services;
    
    internal ApplicationBuilder() => _services = new ServiceCollection();

    public ApplicationBuilder ConfigureServices(Action<IServiceCollection> services)
    {
        _services.AddSingleton<ILoggerFactory, LoggerFactory>();
        _services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
        _services
            .AddLogging(builder => builder
                .AddConsole()
                .ClearProviders()
                .SetMinimumLevel(LogLevel.Information));
        
        _services.AddSingleton<Application>();

        services.Invoke(_services);
        
        return this;
    }
    
    public Application Build()
    {
        var provider = _services.BuildServiceProvider();
        
        return provider.GetRequiredService<Application>();
    }
}

[assembly: InternalsVisibleTo("Microsoft.Extensions.DependencyInjection")]

public class Application
{
    private readonly ILogger<Application> _logger;
    internal Application(ILogger<Application> logger) => _logger = logger;
    
    public static ApplicationBuilder Create() => new();

    public void Run()
    {
        _logger.LogInformation("Application started");
        while (true)
        {
            
        }
    }
}

And the following in ProjectB : ProjectB中的以下内容:

Application.Create()
    .ConfigureServices(services =>
    {
        
    })
    .Build()
    .Run();

I get the following exception: Unhandled exception. System.InvalidOperationException: A suitable constructor for type 'Application' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.我得到以下异常: Unhandled exception. System.InvalidOperationException: A suitable constructor for type 'Application' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor. Unhandled exception. System.InvalidOperationException: A suitable constructor for type 'Application' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.

I thought [assembly: InternalsVisibleTo("Microsoft.Extensions.DependencyInjection")] would allow DI to construct the type but apparently not.我认为[assembly: InternalsVisibleTo("Microsoft.Extensions.DependencyInjection")]将允许 DI 构造类型,但显然不是。 Is there some way to create my own constructor resolver that can use internal constructors?有没有办法创建我自己的可以使用内部构造函数的构造函数解析器?

Also if I skip this problem and make the constructor public (which I don't want to do), the logger doesn't log anything.此外,如果我跳过这个问题并公开构造函数(我不想这样做),记录器不会记录任何内容。 Am I missing something with the setup for the logger?我是否遗漏了记录器的设置?

Thanks谢谢

EDIT: Turns out AddLogging / ClearProviders() was the problem with the logger, I normally do this when using full .NET hosts to clear out the default framework messages but as they aren't here anyways it was clearing out the console logging provider.编辑:原来 AddLogging / ClearProviders() 是记录器的问题,我通常在使用完整的 .NET 主机来清除默认框架消息时这样做,但因为它们不在这里无论如何它正在清除控制台日志记录提供程序。

Edit: I saw your issue was different, but still, consider this a rewrite for how it can be done.编辑:我看到您的问题有所不同,但仍然认为这是对如何完成的重写。

First of all, remember Application is from System.Windows namespace.首先,记住Application来自System.Windows命名空间。 So I wouldn't use it.所以我不会使用它。 Further below, I'll rewrite the code with some other name.在下面,我将用其他名称重写代码。


 internal Application(ILogger<Application> logger) => _logger = logger;

Why not removing the internal keyword as a whole alongside the code above?为什么不将internal关键字作为一个整体与上面的代码一起删除呢? Let's try rewriting it in a way you don't need to do crazy internal witchcraft.让我们尝试以一种你不需要做疯狂的内部巫术的方式重写它。


A better approach更好的方法

Interfaces:接口:

They can be set up in Project B or in a standalone Abstractions project shared by both Project A and Project B .它们可以在Project B中设置,也可以在Project AProject B共享的独立Abstractions项目中设置。

The following is an interface for configuring services (which returns the second interface when calling ConfigureServices :下面是配置服务的接口(调用ConfigureServices时返回第二个接口:

/// <summary>
/// Configures the service application and returns the service built.
/// </summary>
/// <typeparam name="TApplication">Application Type</typeparam>
public interface IAppBuilderConfigureServices<TApplication>
    where TApplication: class
{
    /// <summary>
    /// Creates a service injection container.
    /// </summary>
    /// <param name="services">Opts for configuring services.</param>
    /// <returns>App Service Builder</returns>
    IAppBuildService<TApplication> ConfigureServices(Action<IServiceCollection> services);
}

Interface for building the service:构建服务的接口:

/// <summary>
/// Builds the configuration and gets <see cref="TApplication"/> from container.
/// </summary>
/// <typeparam name="TApplication">Application Type</typeparam>
public interface IAppBuildService<TApplication>
    where TApplication: class
{
    /// <summary>
    /// App Service builder that returns Singleton of <see cref="TApplication"/>
    /// </summary>
    /// <returns>Instance of <see cref="TApplication"/></returns>
    TApplication Build();
}

Project A:项目一:

internal application builder: internal应用程序生成器:


/// <summary>
/// Internally builds the service application and returns the service built.
/// </summary>
/// <typeparam name="TApplication">Application Type</typeparam>
internal class AppBuilder<TApplication> : IAppBuilderConfigureServices<TApplication>, IAppBuildService<TApplication>
    where TApplication: class
{
    private readonly IServiceCollection _services = new ServiceCollection();
    
    public IAppBuildService<TApplication> ConfigureServices(Action<IServiceCollection> services)
    {
        _services.AddLogging(s => s.ClearProviders().AddConsole().SetMinimumLevel(LogLevel.Debug));
        _services.AddSingleton<TApplication>();

        services.Invoke(_services);
        return this;
    }

    public TApplication Build() => _services.BuildServiceProvider().GetRequiredService<TApplication>();
}

public static class AppBuilder
{
    /// <summary>
    /// Creates an instance of <see cref="IAppBuilderConfigureServices{TApplication}"/>
    /// </summary>
    /// <typeparam name="TApplication">Application Type</typeparam>
    /// <returns>Application builder</returns>
    public static IAppBuilderConfigureServices<TApplication> Create<TApplication>()
        where TApplication : class =>
        new AppBuilder<TApplication>();
}

Project B:项目B:

Just of an example of how it MyApp can be configured.只是一个如何配置MyApp的示例。


public static class ProjectB
{
    public static MyApp Initialize()
    {
        return AppBuilder.Create<MyApp>()
            .ConfigureServices(config =>
            {
                // ...
            })
            .Build();
    }
}

Finally, your application code:最后,您的应用程序代码:

public class MyApp
{
    private readonly ILogger<MyApp> _logger;

    public MyApp(ILogger<MyApp> logger) => _logger = logger;

    public void HelloWorld()
    {
        _logger.LogInformation("Hello, World!");
    }
}

This is a draft but I think you get the idea.这是一个草稿,但我想你明白了。 I'm using interfaces to make it more readable, sorry about the summaries, as I wanted to demonstrate what things were going to do.我正在使用接口使其更具可读性,对摘要感到抱歉,因为我想演示将要做什么。


Usage:用法:

ProjectB.Initialize().HelloWorld();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM