2014-10-02 11 views
14

Structuremap을 사용하는 ASP MVC 4 앱이 있습니다. Structuremap 차단을 통해 내 응용 프로그램에 로깅을 추가하려고합니다. 레지스트리에서 , 나는 기본 규칙과의 모든 유형을 등록하기 위해 특정 어셈블리를 스캔 :레지스트리 스캔 유형에 대한 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 수 있도록 레지스트리에 스캔 된 모든 종류의 로깅 프록시를 만들려고합니다. 이 방법이 있습니까?

+0

작동 시키셨습니까? –

+0

불행히도 없습니다. 각 플러그인 유형마다 수동으로 추가했습니다. – rinat

답변

8

쉽게 확장 할 수있는 것처럼 보이지 않지만 사용자 지정 규칙을 사용하여 상당히 괜찮은 솔루션을 사용하고 있습니다. 내가 한 결정을 이해하는 데 도움이되도록 몇 단계 (건너 뛴 많은 실수를 건너 뛰기)를 진행합니다.

먼저 이미 사용중인 DefaultConvention를 살펴보십시오.

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 <>()에 대해서만 호출 할 수 있습니다. For()가 아닌 <>()을 사용하십시오.

다음에서 볼 수 있습니다 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 
{ 

} 

하지만 작동 :

마지막으로, 그것을 증명하기 위해 몇 가지 단위 테스트 작동 당신이 필요로하는 것은 그것을하는 매우 합리적인 방법입니다.

다른 질문이 있으시면 문의하십시오.

+0

이전 릴리스를 되돌려 야했던 것처럼 오래된 strucutremap 코드를 리팩토링 할 시간이 필요합니다. 이것은 훨씬 더 많은 코드이며, 간단하게이 기능을 제거한 것이 이상하게 보입니다. – Simon

+0

이것은 화격자 답을위한 나의 감사 주간 그리고 코드를 걷는다. 나는 최신 버전의 structuremap (v4.0.30319)을 사용하고 있는데, 이는 DefaultConventionWithProxyScanner 클래스의 ScanTypes 메소드를 함축적으로 요구했지만, TypeSet 및 모든 프로세스 메소드가 저에게 유용했습니다. – TechLiam

+0

아 나는 또한 IRepository 과 같은 일반 클래스에 대한 오류가 발생하지만 인스턴스에 대해 생성 된 인스턴스가 없기 때문에이 코드를 로그에 남기고 싶지는 않지만 다른 사람들은이 문제를 해결할 방법을 알아야 할 수도 있습니다. – TechLiam