StructureMap Generic Ctor命名实例

时间:2017-12-04 22:47:59

标签: c# .net generics dependency-injection structuremap

更新

使用以下代码解决了这个问题,但它并不是我正在寻找的解决方案。 对于更通用的解决方案,这仍然是一个公开的奖励。如果我们有一个表格不是intstring的关键值我们&#39 ; ll必须手动添加以使其正常工作。

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类型。

类和接口定义

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界面。

2 个答案:

答案 0 :(得分:3)

想到一个选项:

   <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.yourapp.bundleID</string>
    </array>
</dict>
</plist>

更新。正如我们在评论中指出的那样,当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<>传递泛型类型参数。也许值得在他们的github解决一个问题。你可以像这样解决它:

LogDifferencesCommand

然后做

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; }
}

它有效,但我不能说我喜欢它。

答案 1 :(得分:2)

StructureMap(我)的作者强烈建议您尝试在应用程序引导时预先使用条件注册,通过检查配置值来选择默认的记录器注册,然后只允许自动连接在运行时处理依赖项。 / p>