您可以将Autofac的Owned与标记界面一起使用吗?

时间:2019-07-22 18:39:06

标签: c# autofac

我想使用Autofac为给定的工作单元创建一个或多个WCF通道的新实例。我想使用命令模式来表示工作单元,即给定的命令类注入了所需的通道并实现了许多相关操作。

我尝试了以下操作:

interface IUnitOfWork
{

}

class FooCall : IUnitOfWork
{
    readonly BarChannel _channel;

    public FooCall(BarChannel channel)
    {
        Console.WriteLine($"FooCall({new {channel}})");
        _channel = channel;
    }

    public string Foo()
    {
        return "FOO";
    }
}

class BarChannel
{
    public BarChannel()
    {
        Console.WriteLine("BarChannel()");
    }
}

class FooService
{
    Func<Owned<FooCall>> _helperFn;

    public FooService(Func<Owned<FooCall>> helperFn)
    {
        _helperFn = helperFn;
    }

    public void CallFoo()
    {
        using (var helper = _helperFn())
        {
            Console.WriteLine($"CallFoo(), helper={helper}");
            helper.Value.Foo();
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<BarChannel>().InstancePerOwned<IUnitOfWork>();
        builder.RegisterType<FooCall>().AsImplementedInterfaces().AsSelf();
        builder.RegisterType<FooService>();

        using (var scope = builder.Build().BeginLifetimeScope())
        {
            Console.WriteLine("call 1");
            scope.Resolve<FooService>().CallFoo();
            Console.WriteLine("call 2");
            scope.Resolve<FooService>().CallFoo();
        }
    }
}
简而言之:服务方法创建了一个拥有的工作单元;工作单元将注入一个其称为的自有渠道。该代码示例应显示正在创建的两个通道实例。

除了似乎为拥有的依存关系创建的生存期作用域仅使用解析依存关系的类型进行标记-即FooCall,而不是IUnitOfWork。如果我将BarChannel注册为InstancePerOwned<FooCall>,则代码有效。按原样注册为InstancePerOwned<IUnitOfWork>的它无法解析FooService,因为找不到匹配的生存期范围。我是否缺少某些东西,或者我想使用Autofac做些什么?我宁愿不必将所有WCF通道注册为每个命令类的实例所有者,这似乎很冗长。另一个解决方法是使用每个实例的实例并直接解析Func,但这不会让我说重复使用通道及其之间的依赖关系时的工作单元。

1 个答案:

答案 0 :(得分:2)

问题在于InstancePerOwned<T>实际上只是InstancePerMatchingLifetimeScope(params object[] lifetimeScopeTag)的特例,其中的范围用typeof(T)之类的东西标记。就目前情况而言,尝试解析时,此处提供的标记与附加到作用域的标记之间必须存在一个 direct 链接,该链接始终设置为该特定{{1 }}依赖性。此时,没有其他逻辑可以暗示类型之间的关系,这只是标签上的直接匹配。

但是,Owned<>确实允许指定多个标签,因此可以执行以下操作:

InstancePerMatchingLifetimeScope

要更简洁地包装起来,可以使用:

builder.RegisterType<BarChannel>()
    .InstancePerMatchingLifetimeScope(new TypedService(typeof(FooCall)),new TypedService(typeof(AnotherUnitOfWork))); 

,然后是新的扩展方法:

private static IEnumerable<Type> GetTypesImplementingInterface<TInterface>()
{
    return AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(s => s.GetTypes())
        .Where(p => typeof(TInterface).IsAssignableFrom(p));
}

用法将是:

public static class AutofacRegistrationBuilderExtensions
{
    public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InstancePerOwned<TLimit, TActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> builder, IEnumerable<Type> serviceTypes)
    {
        return builder.InstancePerMatchingLifetimeScope(serviceTypes.Select(s => new TypedService(s)).ToArray());
    }
}

我不确定是否值得在最后一部分介绍Autofac本身,但是我想如果这样做的话,最好将上述两种方法结合在一起并从现有注册中检索适用的类型列表,例如像

builder.RegisterType<BarChannel>().InstancePerOwned(GetTypesImplementingInterface<IUnitOfWork>());

或者,扩展匹配范围逻辑以在解析时检查类型之间的关系可能会有些混乱,因为并非所有标记都属于Type类型。