Structuremap 拦截器用于注册扫描类型

15

我有一个使用Structuremap的ASP MVC 4应用程序。 我正在尝试通过Structuremap拦截来向我的应用程序添加日志记录。 在注册表中,我扫描特定的程序集,以便使用默认约定注册所有类型:


我有一个使用Structuremap的ASP MVC 4应用程序。我试图通过Structuremap拦截来添加应用程序日志记录。在注册表中,我扫描特定的程序集,以便将其所有类型与默认约定一起注册。
public class ServicesRegistry : Registry
{
    public ServicesRegistry()
    {
        Scan(x =>
        {
            x.AssemblyContainingType<MyMarkerService>();
            x.WithDefaultConventions();
        });
    }
}

拦截器:

public class LogInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        var watch = Stopwatch.StartNew();
        invocation.Proceed();
        watch.Stop();//log the time
    }
}

我可以像这样为一个特定的插件类型添加拦截器:

var proxyGenerator = new ProxyGenerator();
container.Configure(x => x.For<IServiceA>().Use<ServiceA>().DecorateWith(instance => proxyGenerator.CreateInterfaceProxyWithTarget(instance, new LogInterceptor())));

但我希望StructureMap能为注册表中扫描到的所有类型创建日志代理。是否有实现这一点的方法?


你成功了吗? - Null Head
很遗憾,我为每个插件类型手动添加了它们。 - rinat
1个回答

9

看起来没有易于扩展的扩展点,但我使用自定义约定实现了一个相当不错的解决方案。为了帮助您理解我做出的决策,我将通过几个步骤向您展示(跳过我在途中犯的许多错误)。

首先让我们看一下您已经在使用的DefaultConvention。

默认约定:

 public class DefaultConventionScanner : ConfigurableRegistrationConvention
{
    public override void Process(Type type, Registry registry)
    {
        if (!TypeExtensions.IsConcrete(type))
            return;
        Type pluginType = this.FindPluginType(type);
        if (pluginType == null || !TypeExtensions.HasConstructors(type))
            return;
        registry.AddType(pluginType, type);
        this.ConfigureFamily(registry.For(pluginType, (ILifecycle)null));
    }

    public virtual Type FindPluginType(Type concreteType)
    {
        string interfaceName = "I" + concreteType.Name;
        return Enumerable.FirstOrDefault<Type>((IEnumerable<Type>)concreteType.GetInterfaces(), (Func<Type, bool>)(t => t.Name == interfaceName));
    }
}

很简单,我们获取类型和接口对并检查它们是否有构造函数,如果有,我们注册它们。很好修改为调用DecorateWith,但只能在For<>().Use<>()上调用,而不能在For().Use()上调用。
接下来让我们看一下DecorateWith的作用:
public T DecorateWith(Expression<Func<TPluginType, TPluginType>> handler)
{
  this.AddInterceptor((IInterceptor) new FuncInterceptor<TPluginType>(handler, (string) null));
  return this.thisInstance;
}

因此,这创建了一个FuncInterceptor并注册了它。我花了很多时间尝试用反射动态创建其中之一,然后决定只需创建一个新类更容易:

public class ProxyFuncInterceptor<T> : FuncInterceptor<T> where T : class
{
    public ProxyFuncInterceptor() : base(x => MakeProxy(x), "")
    {
    }

    protected ProxyFuncInterceptor(Expression<Func<T, T>> expression, string description = null)
        : base(expression, description)
    {
    }

    protected ProxyFuncInterceptor(Expression<Func<IContext, T, T>> expression, string description = null)
        : base(expression, description)
    {
    }

    private static T MakeProxy(T instance)
    {
        var proxyGenerator = new ProxyGenerator();
        return proxyGenerator.CreateInterfaceProxyWithTarget(instance, new LogInterceptor());
    }
}

这个类使得当我们的类型是变量时更��易处理。

最后我基于默认约定制定了自己的约定。

public class DefaultConventionWithProxyScanner : ConfigurableRegistrationConvention
{
    public override void Process(Type type, Registry registry)
    {
        if (!type.IsConcrete())
            return;
        var pluginType = this.FindPluginType(type);
        if (pluginType == null || !type.HasConstructors())
            return;
        registry.AddType(pluginType, type);
        var policy = CreatePolicy(pluginType);
        registry.Policies.Interceptors(policy);

        ConfigureFamily(registry.For(pluginType));
    }

    public virtual Type FindPluginType(Type concreteType)
    {
        var interfaceName = "I" + concreteType.Name;
        return concreteType.GetInterfaces().FirstOrDefault(t => t.Name == interfaceName);
    }

    public IInterceptorPolicy CreatePolicy(Type pluginType)
    {
        var genericPolicyType = typeof(InterceptorPolicy<>);
        var policyType = genericPolicyType.MakeGenericType(pluginType);
        return (IInterceptorPolicy)Activator.CreateInstance(policyType, new object[]{CreateInterceptor(pluginType), null});     
    }

    public IInterceptor CreateInterceptor(Type pluginType)
    {
        var genericInterceptorType = typeof(ProxyFuncInterceptor<>);
        var specificInterceptor = genericInterceptorType.MakeGenericType(pluginType);
        return (IInterceptor)Activator.CreateInstance(specificInterceptor);
    }
}

除了一个额外的改动,它几乎与原来完全相同。我为我们注册的每种类型创建了一个拦截器和拦截器类型。然后我注册了该策略。

最后,还需要一些单元测试来证明它起作用:

 [TestFixture]
public class Try4
{
    [Test]
    public void Can_create_interceptor()
    {
        var type = typeof (IServiceA);
        Assert.NotNull(new DefaultConventionWithProxyScanner().CreateInterceptor(type));
    }

    [Test]
    public void Can_create_policy()
    {
        var type = typeof (IServiceA);
        Assert.NotNull(new DefaultConventionWithProxyScanner().CreatePolicy(type));
    }

    [Test]
    public void Can_register_normally()
    {
        var container = new Container();
        container.Configure(x => x.Scan(y =>
        {
            y.TheCallingAssembly();
            y.WithDefaultConventions();
        }));

        var serviceA = container.GetInstance<IServiceA>();
        Assert.IsFalse(ProxyUtil.IsProxy(serviceA));
        Console.WriteLine(serviceA.GetType());
    }

    [Test]
    public void Can_register_proxy_for_all()
    {
        var container = new Container();
        container.Configure(x => x.Scan(y =>
        {
            y.TheCallingAssembly();
            y.Convention<DefaultConventionWithProxyScanner>();
        }));

        var serviceA = container.GetInstance<IServiceA>();
        Assert.IsTrue(ProxyUtil.IsProxy(serviceA));
        Console.WriteLine(serviceA.GetType());
    }

    [Test]
    public void Make_sure_I_wait()
    {
        var container = new Container();
        container.Configure(x => x.Scan(y =>
        {
            y.TheCallingAssembly();
            y.Convention<DefaultConventionWithProxyScanner>();
        }));

        var serviceA = container.GetInstance<IServiceA>();
        serviceA.Wait();
    }
}
}

 public interface IServiceA
{
    void Wait();
}

public class ServiceA : IServiceA
{
    public void Wait()
    {
       Thread.Sleep(1000);
    }
}

public interface IServiceB
{

}

public class ServiceB : IServiceB
{

}

这里肯定有一些需要整理的地方(缓存,保持DRY,增加更多测试,使其更易于配置),但这对你需要的东西是有效的并且是一个相当合理的方法。
如果您对此有任何其他问题,请提出。

这看起来不错,我需要时间来重构旧的strucutremap代码,因为我不得不回滚到旧版本。这是更多的代码,而且它似乎奇怪他们已经删除了简单执行此操作的能力。 - Simon
这让我感到非常开心,感谢你提供了出色的答案并且详细解释了代码。我正在使用较新版本的StructureMap(v4.0.30319),它要求我在DefaultConventionWithProxyScanner类中实现ScanTypes方法,但我只需要循环遍历TypeSet并调用所有Process方法即可,这对我很有效。 - TechLiam
哦,我也遇到了泛型类的错误,比如IRepository<T>,它们不能被创建实例,但是因为我不想记录那段代码,所以我没事,但其他人可能需要知道解决这个问题的方法。 - TechLiam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接