简体   繁体   English

在.NET Core内置DI容器中为AddScoped服务实现自定义处理

[英]Implement custom dispose for AddScoped services in .NET Core built-in DI Container

I want to register a TelemetryClient in a ServiceCollection using the AddScoped method and call Flush when the client is disposed. 我想使用AddScoped方法在ServiceCollection中注册TelemetryClient ,并在处理客户端时调用Flush I don't have access to the TelemetryClient after the scope is finished to call it explicitly, and also I need to pass the TelemetryClient to a method of a third party method so I cannot use a wrapper. 范围完成后,我将无权访问TelemetryClient ,并且必须将TelemetryClient传递给第三方方法,因此无法使用包装器。

I'm using the built-in Di container of .Net Core in Azure Functions. 我在Azure Functions中使用.Net Core的内置Di容器。

I'm registering it like this: 我正在这样注册:

services.AddScoped(x =>
            {
                return new TelemetryClient();
            });

I would like to have a method like OnRelease in Autofac, so I could register it like the following. 我想在Autofac中有一个类似OnRelease的方法,所以我可以像下面这样注册它。 Notice the call to OnRelease : 注意对OnRelease的调用:

services.AddScoped(x =>
            {
                return new TelemetryClient();
            }).OnRelease(x=>
            {
                x.Flush();
            });

You can wrap the TelemetryClient like this: 您可以像这样包装TelemetryClient

public interface ILogsStuff
{
    void LogSomething(string message);
}

public class TelemetryClientLogger : ILogsStuff, IDisposable
{
    private readonly TelemetryClient _telemetryClient;

    public TelemetryClientLogger(TelemetryClient telemetryClient)
    {
        _telemetryClient = telemetryClient;
    }

    public void LogSomething(string message)
    {
        _telemetryClient.TrackTrace(message);
    }

    public void Dispose()
    {
        _telemetryClient.Flush();
    }
}

There's an added benefit, which is that whatever class you're injecting this into won't depend directly on TelemetryClient . 还有一个额外的好处,那就是,无论您将其注入哪个类,都不会直接依赖于TelemetryClient You can define an interface that describes whatever you want to use the client for, and that's easier to both mock and replace with some other implementation if needed. 您可以定义一个接口,该接口描述要使用客户端的任何对象,并且如果需要,可以更轻松地模拟和替换为其他实现。

The suggestion was made to make TelemetryClient implement IDisposable and have it call Flush() when disposed, and this was the recommended solution. 提出了使TelemetryClient实现IDisposable建议,并使其在处置时调用Flush() ,这是推荐的解决方案。


The update to your question says: 您问题的更新内容为:

I need to pass the TelemetryClient to a method of a third party method so I cannot use a wrapper. 我需要将TelemetryClient传递给第三方方法的方法,所以我不能使用包装器。

That changes things somewhat, as now the intention is not to use the TelemetryClient but to resolve it and then pass it to a method of a 3rd-party library. 事情有所改变,因为现在的目的不是使用 TelemetryClient而是解决它,然后将其传递给第三方库的方法。 That's peculiar because it means that this library forces code that uses it to have a hard dependency on TelemetryClient , and then presumably doesn't handle that client as expected. 这很特殊,因为这意味着该库强制使用该库的代码对TelemetryClient具有硬依赖性,然后大概无法按预期方式处理该客户端。

At this point there are still solutions, but they're all ugly because they are solutions to a weird problem that shouldn't exist (and wasn't created by your code.) 在这一点上,仍然存在解决方案,但是它们都是丑陋的,因为它们是解决不应该存在的怪异问题的解决方案(不是由您的代码创建的)。

If you're passing TelemetryClient to a method, that method writes to it, and then it must be flushed, why doesn't that library flush it? 如果将TelemetryClient传递给方法,该方法将写入该方法,然后必须对其进行刷新,为什么该库不刷新它?

You can likely solve that problem by passing the TelemetryClient to the method when you execute it and then flushing the client after the method executes. 您可以通过在执行时将TelemetryClient传递给方法,然后在方法执行后刷新客户端,来解决该问题。

CallSomeMethod(someArgument, _telemtryClient);
telemetryClient.Flush();

Or, to get the result closer to your question, you could do this, which would obtain the result but is still awkward and weird: 或者,为了使结果更接近您的问题,您可以这样做,虽然可以获得结果,但仍然很尴尬和怪异:

public class TelemetryClientWrapper : IDisposable
{
    private readonly TelemetryClient _telemetryClient;

    public TelemetryClientWrapper(TelemetryClient telemetryClient)
    {
        _telemetryClient = telemetryClient;
    }

    public TelemetryClient Client => _telemetryClient;

    public void Dispose()
    {
        _telemetryClient.Flush();
    }
}

You can inject this class and pass it to the library's method like this: 您可以像下面这样注入该类并将其传递给库的方法:

CallSomeMethod(someArgument, _telemtryClientWrapper.Client);

The problem now is that your class has a hard dependency on TelemetryClient even though it doesn't use it , all so it can fix what the other library (presumably) isn't doing correctly with that client. 现在的问题是,即使您的类不使用 TelemetryClient 它也具有严格的依赖关系,因此它可以修复其他库(大概是)对该客户端未正确执行的操作。

Having said all of that, perhaps in your scenario this is the answer : 说了这么多,也许在您的情况下, 这就是答案

The built-in service container is meant to serve the needs of the framework and most consumer apps. 内置服务容器旨在满足框架和大多数消费者应用程序的需求。 We recommend using the built-in container unless you need a specific feature that it doesn't support. 我们建议您使用内置容器,除非您需要它不支持的特定功能。 Some of the features supported in 3rd party containers not found in the built-in container: 内置容器中找不到第三方容器支持的某些功能:

In other words, if you need what Autofac does, you can still use Autofac with IServiceCollection . 换句话说,如果您需要Autofac做什么,您仍然可以将AutofacIServiceCollection 一起使用。

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

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