依赖注入解决数据库导出的正确实现
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 建议的方法更多的测试。他的解决方案有更多的代码并且需要更少的测试。动态不需要额外的代码,但需要更多的测试代码。
我想在数据库中存储多个对象。对象的类型不固定,可以由加载的模块动态添加。在我的应用程序的一部分,我想搜索正确的实现以使用创建的对象调用方法。
一个例子。我有 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 建议的方法更多的测试。他的解决方案有更多的代码并且需要更少的测试。动态不需要额外的代码,但需要更多的测试代码。