重新创建对象和 DI

Recreating an object and DI

我正在编写一个 class,它与 API 客户端对象一起工作,该对象有时会损坏,必须从使用它的对象内部重新创建。使用依赖注入执行此操作的最佳方法是什么?我对从 class 内部调用 DI 框架犹豫不决,因为它使我的代码依赖于它。

public class MyObject
{
    protected IMyAPIClient Client { get; set; }

    public MyObject(IMyAPIClient client)
    {
        Client = client;
    }

    protected async Task<ReturnType> Run<ReturnType>(Func<Task<ReturnType>> action, int attempt = 1)
    {
        try
        {
            return await action();
        }
        catch(Exception exception)
        {
            Client = await GetNewClient();
            if(attempt > MAX_ATTEMPTS)
            {
                throw new Exception($"Failed {attempt} times", exception);
            }
            
            return await Run(action, attempt++);
        }
    }

    protected async Task<IMyAPIClient> GetNewClient()
    {
        // what to do here?
    }
}

我想出的一个解决方案是在知道 IMyAPIClient 类型的 class 中实现 IMyAPIClient 并重新创建它,从而绕过 DI 框架.我想知道这是否明智,或者是否有更好的方法来做到这一点?

我会首先尝试解决 ApiClient 的问题。对有缺陷的代码使用变通办法很少是一个好主意,但有时可能需要对第三方代码使用。如果问题出在第三方代码中,也可能建议将其放在单独的进程中,否则您无法知道失败是否会产生一些意想不到的副作用。

要创建新对象,我建议注入某种工厂。具体如何完成将取决于 DI/IoC 框架。最简单的可能是 Func<IMyAPIClient>,但替代方案是显式工厂 -class。您可以选择工厂是否应该使用 IoC 容器来构造对象,或者直接构造它。此外,一些 IoC 框架将以某种特殊方式处理工厂,而其他框架可能要求工厂像其他任何东西一样进行注册。查看您的框架的文档,了解应如何管理工厂。

如果可能,我还建议将重启逻辑移至装饰器。这样它就与使用分离,并且在有多个用户或需要更新逻辑的情况下应该更加灵活。然而,注册装饰器可能有点棘手以确保它们正常工作,但这也取决于您的 IoC 框架。

我通过创建一个创建 IMyAPIClient 的工厂 class 解决了这个问题。 这样我就可以在这个工厂中处理依赖注入 class 而无需使其他代码依赖于 DI 框架。

public class MyObject
{
    protected IMyAPIClientFactory ClientFactory { get; set; }

    public MyObject(IMyAPIClientFactory clientFactory)
    {
        ClientFactory = clientFactory;
        Client = clientFactory.CreateClient();
    }

    protected async Task<ReturnType> Run<ReturnType>(Func<Task<ReturnType>> action, int attempt = 1)
    {
        try
        {
            return await action();
        }
        catch(Exception exception)
        {
            Client = await clientFactory.CreateClient();
            if(attempt > MAX_ATTEMPTS)
            {
                throw new Exception($"Failed {attempt} times", exception);
            }
            
            return await Run(action, attempt++);
        }
    }
}