依赖注入解决数据库导出的正确实现

Dependency Injection resolve correct implementation for database export

我想在数据库中存储多个对象。对象的类型不固定,可以由加载的模块动态添加。在我的应用程序的一部分,我想搜索正确的实现以使用创建的对象调用方法。

一个例子。我有 3 个对象:

public class Employee : Person { }
public class Supervisor : Person { }
public abstract class Person { }

并且在 DI 容器中注册了 IExporter 的实现:

public interface IExporter<T> where T: Person
{
    Task ExportAsync(T person);
}

public class EmployeeExporter : IExporter<Employee>
{
    public Task ExportAsync(Employee exployee) => Task.CompletedTask; // TODO
}

public class SupervisorExporter : IExporter<Supervisor>
{
    public Task ExportAsync(Supervisor supervisor) => Task.CompletedTask; // TODO
}

我个人工厂 returns 个人如何知道选择哪个出口商是正确的出口商?

var type = typeof(IExporter<>).MakeGenericType(person.GetType());

var exporter = (IExporter<Employee>)serviceProvider.GetRequiredService(type);

await exporter.ExportAsync(person);

类似这样,但没有明确指定 IExporter<Employee> 转换。

还是我完全错了?

我已经提到了 Jimmy Bogard 的 article,所以我不再重复。除了Jimmy提到的选项之外,还有一个选项,就是使用C# dynamic。这看起来像这样:

var type = typeof(IExporter<>).MakeGenericType(person.GetType());

dynamic exporter = serviceProvider.GetRequiredService(type);

await exporter.ExportAsync((dynamic)person);

在运行时,C# 编译器会寻找名为 ExportAsync 的方法。这是您可以获得的最简洁的解决方案。但请注意以下缺点:

  • 重构: 这不是重构安全的。如果重构 IExporter<T> 接口,此代码会继续编译但在运行时会失败。您应该为您的工厂添加一个单元测试以确保它仍然有效。
  • Public: 使用 dynamic 关键字,您只能在 public [=40] 上调用 public 方法=].即使您的 IExporter<T> 定义为 public,当导出器实现(或您决定包装它的最外层装饰器)是 internal 时,调用也会失败。对我来说,这似乎是 C# 编译器中的一个怪癖,因为 IMO 它应该能够在其接口为 public 时调用 ExportAsync,但事实并非如此。同样,您可能需要添加一些单元测试以确保其正常工作。

使用 dynamic 的结果是,您将添加比 Jimmy 建议的方法更多的测试。他的解决方案有更多的代码并且需要更少的测试。动态不需要额外的代码,但需要更多的测试代码。