Autofac和合同类

时间:2015-04-07 19:44:51

标签: c# dependency-injection autofac code-contracts

假设我们有以下内容:

[ContractClass(typeof(ContractClassForIFoo))]
public interface IFoo
{
    int DoThing(string x);
}

public class Foo : IFoo { ... }

[ContractClassFor(typeof(IFoo))]
public class ContractClassForIFoo : IFoo
{
    public int DoThing(string x)
    {
        Contract.Requires<ArgumentNullException>(x != null);
        return 0;
    }
}

我正在使用Autofac来注册实现IFoo的所有组件:

builder.RegisterAssemblyTypes(ThisAssembly).As<IFoo>();

当我稍后用以下方法解决我的依赖关系时:

var dependencies = container.Resolve<IFoo[]>();

我应该获得所有实现IFoo 的类,除了契约类。如何防止所有我的合同类解析而不将它们完全移动到单独的程序集中?

我可以做类似的事情:

builder.RegisterAssemblyTypes(ThisAssembly)
    .Where(t=> t.GetCustomAttribute<ContractClassForAttribute>() == null)
    .As<IFoo>();

但我需要为每个组件注册执行此操作。影响所有注册的东西会更好。是否可以对从Autofac解析的类型进行全局排除,如果它们具有ContractClassForAttribute属性?

2 个答案:

答案 0 :(得分:1)

编辑正如Steven评论中所解释的那样,ContractClassContractClassFor标有[Conditional("CONTRACTS_FULL")],此解决方案可能会为这些属性引入错误。见史蒂文&#39;评论以获得更好的解释。


我不知道任何允许对RegisterAssemblyTypes方法注册的注册进行全局过滤的机制。使用此方法过滤注册的唯一解决方案是使用代码示例中显示的Where方法。

当注册在ComponentRegistry内注册时,无法将其从注册表中删除。

如果您不想在每次注册时使用Where方法,则可以创建另一种方法。

public static class ContractClassRegistrationExtensions
{
    public static IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> NotContractClass<TLimit, TScanningActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration) where TScanningActivatorData : ScanningActivatorData
    {
        if (registration == null)
        {
            throw new ArgumentNullException("registration");
        }

        return registration.Where(t => t.GetCustomAttribute<ContractClassForAttribute>() == null); 
    }
}

使用此方法,而不是

builder.RegisterAssemblyTypes(ThisAssembly)
       .Where(t=> t.GetCustomAttribute<ContractClassForAttribute>() == null)
       .As<IFoo>();

你可以写:

builder.RegisterAssemblyTypes(ThisAssembly)
       .NotContractClass()
       .As<IFoo>();

它不是真正的解决方案,但它是我在类似情况下使用的解决方案。

顺便说一句,如果您真的想要使用 Autofac 进行一些魔术,那么您可以实现IRegistrationSource

public class FilterRegistrationSource : IRegistrationSource
{
    private static MethodInfo _createFilteredRegistrationMethod = typeof(FilterRegistrationSource).GetMethod("CreateFilteredRegistration");

    public Boolean IsAdapterForIndividualComponents
    {
        get
        {
            return false;
        }
    }

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType serviceWithType = service as IServiceWithType;

        if (serviceWithType == null)
        {
            yield break;
        }

        Type serviceType = serviceWithType.ServiceType;
        if (!serviceType.IsClosedTypeOf(typeof(IEnumerable<>)))
        {
            yield break;
        }
        Type elementType = new Type[] { serviceType }.Concat(serviceType.GetInterfaces())
                                      .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                                      .Select(t => t.GetGenericArguments()[0])
                                      .First();

        yield return (IComponentRegistration)FilterRegistrationSource._createFilteredRegistrationMethod.MakeGenericMethod(elementType)
                                                                     .Invoke(this, new Object[] { serviceWithType });
    }

    public IComponentRegistration CreateFilteredRegistration<T>(IServiceWithType serviceWithType)
    {
        return RegistrationBuilder.ForDelegate((cc, p) => cc.ComponentRegistry
                                                            .RegistrationsFor(serviceWithType.ChangeType(typeof(T)))
                                                            .Where(r => !r.Activator.LimitType.GetCustomAttributes(typeof(ContractClassForAttribute), false).Any())
                                                            .Select(r => r.Activator.ActivateInstance(cc, p))
                                                            .Cast<T>())
                                  .As((Service)serviceWithType)
                                  .CreateRegistration();

    }
}

您可以这样注册:builder.RegisterSource(new FilterRegistrationSource())

我没有测试过此解决方案的性能损失,请谨慎使用。

另一个有趣的解决方案是使用AOP来自定义注册注册的方式。

答案 1 :(得分:0)

解决此问题的更好方法是正确定义合同类。建议您在创建包含程序集合同的类时,该类为privateabstract

[ContractClass(typeof(ContractClassForIFoo))]
public interface IFoo
{
    int DoThing(string x);
}

public class Foo : IFoo { ... }

[ContractClassFor(typeof(IFoo))]
private abstract class ContractClassForIFoo : IFoo
{
    public int DoThing(string x)
    {
        Contract.Requires<ArgumentNullException>(x != null);
        throw new NotImplementedException();
    }
}

现在,该类是private,因此AutoFac应该无法看到它 - 但当然可能因为它可能正在使用反射;但由于它是private,它不应该尝试注册它。除此之外,它是abstract,因此无论如何都无法实例化。这解决了所有问题。

另外,合同类中的所有方法都应throw new NotImplementedException();。这样,如果您忘记将其标记为privateabstract,则所有方法都会抛出。你应该在开发过程中快速发现它。只使用退化形式的方法可能会让你失望。

这是“代码合同”手册和社区推荐的模式。

相关问题