Simple Injector 是否有办法向对象工厂注册条件?
Does Simple Injector have a way to RegisterConditional with object factory?
我明白了Simple Injector's Container has this method
public void RegisterConditional<TService, TImplementation>(
Predicate<PredicateContext> predicate
)
但是我想为不同的服务使用同一个实现的不同对象,所以我需要的重载方法是这样的
public void RegisterConditional<TService>(
Func<TService> instanceCreator,
Predicate<PredicateContext> predicate
)
但是SimpleInjector 没有。我正在尝试找到其他 Container 的方法来注册具有服务条件的实例创建者。
还有其他方法吗?
或者,我想做的不是好的设计,所以开发人员没有实现它?
已编辑:添加示例和更详细的问题。
例子
class CSVFileScanner
{
public CSVFileScanner(IFileLocator fileLocator) { }
}
class XMLFileScanner
{
public XMLFileScanner(IFileLocator fileLocator) { }
}
class DefaultLogFileLocator: ILogFileLocator
{
public DefaultLogFileLocator(string directoryPath, string searchPattern) { }
}
var locatorForCSVFileScanner = new DefaultLogFileLocator("C:\CSVLogDir", "*.csv")
var locatorForXMLFileScanner = new DefaultLogFileLocator("C:\XMLLogDir", "*.xml")
从示例源代码中,如何注册它们以在创建 CSVFileScanner
时将 locatorForCSVFileScanner
对象传递给 CSVFileScanner
构造函数并将 locatorForXMLFileScanner
对象传递给创建 XMLFileScanner
时的 XMLFileScanner
构造函数?
Or, is what I am trying to do not the good design, so the developers don't implement it?
看到您的示例后,我不得不得出结论,可能存在设计缺陷。设计的主要问题是您似乎违反了 Liskov Substitution Principle (LSP)。 LSP 是 SOLID 原则之一,它声明子 类(或接口的实现)应该可以相互互换,而不影响消费者。但是,在您的应用程序中,XMLFileScanner
似乎在随 CSV 文件一起提供时中断。
所以从 LSP 的角度来看,这意味着两个文件扫描器实现都应该有自己的抽象。一旦你给了他们自己的抽象,问题就会完全消失。
如果交换文件定位器对文件扫描器的工作没有影响(例如因为它们不读取,而只是写入),则没有违反 LSP 并且设计没问题。
如果更改抽象不可行或未违反 LSP,一个选项是使用工厂委托或简单地将其创建为单例来注册文件扫描器。这使您可以完全控制对象图那部分的组成。例如:
container.RegisterSingleton<CSVFileScanner>(
new CSVFileScanner(new DefaultLogFileLocator("C:\CSVLogDir", "*.csv")));
container.RegisterSingleton<XMLFileScanner>(
new XMLFileScanner(new DefaultLogFileLocator("C:\XMLLogDir", "*.xml")));
But the SimpleInjector doesn't have it. I am trying to find the other Container's methods to register instance creator with condition for the service. Is there the other ways else I can do?
其实可以使用RegisterConditional
的方法来实现,但是这个功能有点隐蔽,这是故意的。 Simple Injector 试图促进构建在启动阶段完全已知的对象图,不鼓励基于运行时条件构建对象图。 Func<TService> instanceCreator
委托的使用允许创建运行时条件,这就是缺少此类重载的原因。
然而,执行此操作的方法如下:
var csv = Lifestyle.Singleton.CreateRegistration<IFileLocator>(
() => new DefaultLogFileLocator("C:\CSVLogDir", "*.csv"), container);
var xml = Lifestyle.Singleton.CreateRegistration<IFileLocator>(
() => new DefaultLogFileLocator("C:\XMLLogDir", "*.csv"), container);
container.RegisterConditional(typeof(IFileLocator), csv, WhenInjectedInto<CSVFileScanner>);
container.RegisterConditional(typeof(IFileLocator), xml, WhenInjectedInto<XMLFileScanner>);
// Helper method.
static bool WhenInjectedInto<T>(PredicateContext c) =>
c.Consumer.ImplementationType == typeof(T);
我明白了Simple Injector's Container has this method
public void RegisterConditional<TService, TImplementation>(
Predicate<PredicateContext> predicate
)
但是我想为不同的服务使用同一个实现的不同对象,所以我需要的重载方法是这样的
public void RegisterConditional<TService>(
Func<TService> instanceCreator,
Predicate<PredicateContext> predicate
)
但是SimpleInjector 没有。我正在尝试找到其他 Container 的方法来注册具有服务条件的实例创建者。 还有其他方法吗?
或者,我想做的不是好的设计,所以开发人员没有实现它?
已编辑:添加示例和更详细的问题。
例子
class CSVFileScanner
{
public CSVFileScanner(IFileLocator fileLocator) { }
}
class XMLFileScanner
{
public XMLFileScanner(IFileLocator fileLocator) { }
}
class DefaultLogFileLocator: ILogFileLocator
{
public DefaultLogFileLocator(string directoryPath, string searchPattern) { }
}
var locatorForCSVFileScanner = new DefaultLogFileLocator("C:\CSVLogDir", "*.csv")
var locatorForXMLFileScanner = new DefaultLogFileLocator("C:\XMLLogDir", "*.xml")
从示例源代码中,如何注册它们以在创建 CSVFileScanner
时将 locatorForCSVFileScanner
对象传递给 CSVFileScanner
构造函数并将 locatorForXMLFileScanner
对象传递给创建 XMLFileScanner
时的 XMLFileScanner
构造函数?
Or, is what I am trying to do not the good design, so the developers don't implement it?
看到您的示例后,我不得不得出结论,可能存在设计缺陷。设计的主要问题是您似乎违反了 Liskov Substitution Principle (LSP)。 LSP 是 SOLID 原则之一,它声明子 类(或接口的实现)应该可以相互互换,而不影响消费者。但是,在您的应用程序中,XMLFileScanner
似乎在随 CSV 文件一起提供时中断。
所以从 LSP 的角度来看,这意味着两个文件扫描器实现都应该有自己的抽象。一旦你给了他们自己的抽象,问题就会完全消失。
如果交换文件定位器对文件扫描器的工作没有影响(例如因为它们不读取,而只是写入),则没有违反 LSP 并且设计没问题。
如果更改抽象不可行或未违反 LSP,一个选项是使用工厂委托或简单地将其创建为单例来注册文件扫描器。这使您可以完全控制对象图那部分的组成。例如:
container.RegisterSingleton<CSVFileScanner>(
new CSVFileScanner(new DefaultLogFileLocator("C:\CSVLogDir", "*.csv")));
container.RegisterSingleton<XMLFileScanner>(
new XMLFileScanner(new DefaultLogFileLocator("C:\XMLLogDir", "*.xml")));
But the SimpleInjector doesn't have it. I am trying to find the other Container's methods to register instance creator with condition for the service. Is there the other ways else I can do?
其实可以使用RegisterConditional
的方法来实现,但是这个功能有点隐蔽,这是故意的。 Simple Injector 试图促进构建在启动阶段完全已知的对象图,不鼓励基于运行时条件构建对象图。 Func<TService> instanceCreator
委托的使用允许创建运行时条件,这就是缺少此类重载的原因。
然而,执行此操作的方法如下:
var csv = Lifestyle.Singleton.CreateRegistration<IFileLocator>(
() => new DefaultLogFileLocator("C:\CSVLogDir", "*.csv"), container);
var xml = Lifestyle.Singleton.CreateRegistration<IFileLocator>(
() => new DefaultLogFileLocator("C:\XMLLogDir", "*.csv"), container);
container.RegisterConditional(typeof(IFileLocator), csv, WhenInjectedInto<CSVFileScanner>);
container.RegisterConditional(typeof(IFileLocator), xml, WhenInjectedInto<XMLFileScanner>);
// Helper method.
static bool WhenInjectedInto<T>(PredicateContext c) =>
c.Consumer.ImplementationType == typeof(T);