StructureMap 通用 Ctor 命名实例
StructureMap Generic Ctor Named Instance
更新
我用下面的代码解决了这个问题,但这不是我要找的解决方案。 这仍然是一个更通用的解决方案的悬赏。如果我们有一个 table 而不是 int
或 string
我们必须手动添加键值才能使其正常工作。
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"))
.Ctor<string>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
原问题
我有三种类型的记录器,我在我的容器中为它们定义了命名实例:
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>))
.Named("all");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>))
.Named("nlog");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>))
.Named("database");
LogDifferencesCommand
接收一个 ILogDifferencesLogger<>
作为其唯一参数:
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
如何根据应用程序设置正确配置 ILogDifferencesCommand<>
以获取正确的 命名实例 ?现在我有这样的东西:
c.For(typeof(ILogDifferencesCommand<,>))
.Use(typeof(LogDifferencesCommand<,>));
我遇到的问题是我无法引入 Ctor<>
,因为我无法使用具有该签名的未绑定泛型,所以我无法使用 Named
方法为此关闭 Ctor
。
例如,我可以做这样的事情,但这不会影响所有可能的类型:
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
但问题是我必须处理系统使用的每个 TKey
类型。
Class 和接口定义
public class LogDifferencesCommand<TModel, TKey> : ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
}
public interface ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
List<LogDifference> CalculateDifferences(TModel x, TModel y);
void LogDifferences(TModel x, TModel y, string tableName, string keyField, string userId, int? clientId);
void RegisterCustomDisplayNameObserver(WeakReference<ICustomDisplayNameObserver<TModel>> observer);
void RegisterCustomChangeDateObserver(WeakReference<ICustomChangeDateObserver<TModel>> observer);
}
public interface ILogDifferencesLogger<TKey>
{
void LogDifferences(string tableName, string keyField, string userId, TKey id, List<LogDifference> differences, int? clientId);
}
之所以需要 TKey
是因为 IIdModel
界面。
想到的一个选项:
var loggers = new Dictionary<string, ConfiguredInstance>();
loggers.Add("all", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>)));
loggers.Add("nlog", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>)));
loggers.Add("database", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>)));
foreach (var kv in loggers) {
// if you still need them named
// if you only used names for this concrete scenario - you probably don't
// so can remove it
kv.Value.Named(kv.Key);
}
c.For(typeof(LogDifferencesCommand<>))
.Use(typeof(LogDifferencesCommand<>))
// add explicit instance as dependency
.Dependencies.Add(typeof(ILogDifferencesLogger<>), loggers[AppSettingsManager.Get("logDifferences:Target")]);
更新。正如我们在评论中发现的那样,当 LogDifferencesCommand
具有多个类型参数时,这不适用于您的特定情况。出于某种原因(我认为这是一个错误)- 结构图试图创建封闭的泛型类型 ILogDifferencesLogger<>
,但在这样做时 - 从 LogDifferencesCommand
传递泛型类型参数。也许值得向他们 github 提出一个问题。您可以像这样解决它:
public class GenericTypesWorkaroundInstance : Instance
{
private readonly Instance _target;
private readonly Func<Type[], Type[]> _chooseTypes;
public GenericTypesWorkaroundInstance(Instance target, Func<Type[], Type[]> chooseTypes) {
_target = target;
_chooseTypes = chooseTypes;
ReturnedType = _target.ReturnedType;
}
public override Instance CloseType(Type[] types) {
// close type correctly by ignoring wrong type arguments
return _target.CloseType(_chooseTypes(types));
}
public override IDependencySource ToDependencySource(Type pluginType) {
throw new NotSupportedException();
}
public override string Description => "Correctly close types over open generic instance";
public override Type ReturnedType { get; }
}
然后做
commandReg.Dependencies.Add(
commandReg.Constructor.GetParameters().First(
p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(ILogDifferencesLogger<>)).Name,
new GenericTypesWorkaroundInstance(
loggers[AppSettingsManager.Get("logDifferences:Target")],
// specify which types are correct
types => types.Skip(1).ToArray()));
它确实有效,但我不能说我喜欢它。
StructureMap 的作者(我)强烈建议您尝试在应用程序引导时预先使用条件注册 select 默认记录器注册,方法是检查配置值,然后只允许自动连接处理运行时的依赖关系。
更新
我用下面的代码解决了这个问题,但这不是我要找的解决方案。 这仍然是一个更通用的解决方案的悬赏。如果我们有一个 table 而不是 int
或 string
我们必须手动添加键值才能使其正常工作。
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"))
.Ctor<string>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
原问题
我有三种类型的记录器,我在我的容器中为它们定义了命名实例:
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>))
.Named("all");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>))
.Named("nlog");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>))
.Named("database");
LogDifferencesCommand
接收一个 ILogDifferencesLogger<>
作为其唯一参数:
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
如何根据应用程序设置正确配置 ILogDifferencesCommand<>
以获取正确的 命名实例 ?现在我有这样的东西:
c.For(typeof(ILogDifferencesCommand<,>))
.Use(typeof(LogDifferencesCommand<,>));
我遇到的问题是我无法引入 Ctor<>
,因为我无法使用具有该签名的未绑定泛型,所以我无法使用 Named
方法为此关闭 Ctor
。
例如,我可以做这样的事情,但这不会影响所有可能的类型:
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
但问题是我必须处理系统使用的每个 TKey
类型。
Class 和接口定义
public class LogDifferencesCommand<TModel, TKey> : ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
}
public interface ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
List<LogDifference> CalculateDifferences(TModel x, TModel y);
void LogDifferences(TModel x, TModel y, string tableName, string keyField, string userId, int? clientId);
void RegisterCustomDisplayNameObserver(WeakReference<ICustomDisplayNameObserver<TModel>> observer);
void RegisterCustomChangeDateObserver(WeakReference<ICustomChangeDateObserver<TModel>> observer);
}
public interface ILogDifferencesLogger<TKey>
{
void LogDifferences(string tableName, string keyField, string userId, TKey id, List<LogDifference> differences, int? clientId);
}
之所以需要 TKey
是因为 IIdModel
界面。
想到的一个选项:
var loggers = new Dictionary<string, ConfiguredInstance>();
loggers.Add("all", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>)));
loggers.Add("nlog", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>)));
loggers.Add("database", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>)));
foreach (var kv in loggers) {
// if you still need them named
// if you only used names for this concrete scenario - you probably don't
// so can remove it
kv.Value.Named(kv.Key);
}
c.For(typeof(LogDifferencesCommand<>))
.Use(typeof(LogDifferencesCommand<>))
// add explicit instance as dependency
.Dependencies.Add(typeof(ILogDifferencesLogger<>), loggers[AppSettingsManager.Get("logDifferences:Target")]);
更新。正如我们在评论中发现的那样,当 LogDifferencesCommand
具有多个类型参数时,这不适用于您的特定情况。出于某种原因(我认为这是一个错误)- 结构图试图创建封闭的泛型类型 ILogDifferencesLogger<>
,但在这样做时 - 从 LogDifferencesCommand
传递泛型类型参数。也许值得向他们 github 提出一个问题。您可以像这样解决它:
public class GenericTypesWorkaroundInstance : Instance
{
private readonly Instance _target;
private readonly Func<Type[], Type[]> _chooseTypes;
public GenericTypesWorkaroundInstance(Instance target, Func<Type[], Type[]> chooseTypes) {
_target = target;
_chooseTypes = chooseTypes;
ReturnedType = _target.ReturnedType;
}
public override Instance CloseType(Type[] types) {
// close type correctly by ignoring wrong type arguments
return _target.CloseType(_chooseTypes(types));
}
public override IDependencySource ToDependencySource(Type pluginType) {
throw new NotSupportedException();
}
public override string Description => "Correctly close types over open generic instance";
public override Type ReturnedType { get; }
}
然后做
commandReg.Dependencies.Add(
commandReg.Constructor.GetParameters().First(
p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(ILogDifferencesLogger<>)).Name,
new GenericTypesWorkaroundInstance(
loggers[AppSettingsManager.Get("logDifferences:Target")],
// specify which types are correct
types => types.Skip(1).ToArray()));
它确实有效,但我不能说我喜欢它。
StructureMap 的作者(我)强烈建议您尝试在应用程序引导时预先使用条件注册 select 默认记录器注册,方法是检查配置值,然后只允许自动连接处理运行时的依赖关系。