2014-10-02 15 views
14

Yapısal Haritası kullanan bir ASP MVC 4 uygulamasına sahibim. Uygulamamıza Structuremap kesişimi aracılığıyla giriş eklemeye çalışıyorum. Bir Kayıt defterinde , varsayılan kongre ile 's türleri kayıt için belirli bir derleme tarama:Kayıt defteri taranan türler için Structuremap kesişimi

public class ServicesRegistry : Registry 
{ 
    public ServicesRegistry() 
    { 
     Scan(x => 
     { 
      x.AssemblyContainingType<MyMarkerService>(); 
      x.WithDefaultConventions(); 
     }); 
    } 
} 

önleme: Bir belirli eklenti türü için önleme ekleyebilir

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

böyle:

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

ama StructureMap yapmak kayıt tarandı her türlü günlük vekiller oluşturmak istiyorum. Bunu başarmanın bir yolu var mı?

+0

Bunu işe aldınız mı? –

+0

Maalesef hayır. Eklenti türlerimin her biri için bunları manuel olarak ekledim. – rinat

cevap

8

Bunun için kolay bir uzantı noktası var gibi görünmüyor, ancak özel bir sözleşme kullanarak oldukça iyi bir çözümle çalıştım. Yaptığım kararları anlamanıza yardımcı olmak için birkaç adımdan geçeceğim (yolumda yaptığım birçok yanlış adımı atladım).

Öncelikle kullanmakta olduğunuz DefaultConvention'a bakalım.

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

Oldukça basit, biz tipi ve arayüz çift olsun ve onlar biz onları kayıt yaparsak ta, bir kurucuya sahip emin olmak için kontrol edin. DecorateWith'i çağırması için bunu değiştirmek güzel olurdu, ancak bunu yalnızca <>() için kullanabilirsiniz. <>() kullanın, For(). Use().

Sonraki bakalım DecorateWith ne:

public T DecorateWith(Expression<Func<TPluginType, TPluginType>> handler) 
{ 
    this.AddInterceptor((IInterceptor) new FuncInterceptor<TPluginType>(handler, (string) null)); 
    return this.thisInstance; 
} 

Yani bu FuncInterceptor oluşturur ve bunu kaydeder.

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

Bu sınıf sadece daha kolay biz tip varken çalışmayı kolaylaştırır: Ben sadece yeni bir sınıf yapmak kolay olurdu karar vermeden önce bu dinamik yansıması ile birini oluşturmaya çalışan zaman adil biraz geçirdi değişken olarak

Son olarak, Varsayılan sözleşmeye dayalı olarak kendi Sözleşmemizi yaptım.

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

Onun neredeyse tamamen aynı tek eklenmesiyle, biz kayıt her türü için bir önleme ve interceptorType oluşturun. O zaman bu politikayı kaydederim. Bazı (daha kolay yapılandırmak yapmak, daha fazla test, bu KURU yapmak, önbelleğe alma) Buraya temizlemek için kesinlikle yer var

[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 
{ 

} 

ama işe yarar:

Son olarak, bunu kanıtlamak için birkaç ünite testleri işleri ihtiyacınız olan ve bunu yapmanın oldukça mantıklı bir yolu.

Lütfen başka sorularınız mı var?

+0

Bu iyi görünüyor, eski bir sürüm açmak zorunda kaldığım gibi eski strucutremap kodunu yeniden açmak için zamana ihtiyacım var. Bu çok daha fazla kod ve bunu basit bir şekilde yapabilmeleri için tuhaf görünüyorlar. – Simon

+0

Bu sadece benim hizmetçi günlüğü cevabın teşekkürler ve kod rağmen yürümek için teşekkürler. DefaultConventionWithProxyScanner sınıfında ScanTypes yöntemini karıştırmamı gerektiren structuremap'ın daha yeni bir sürümünü kullanıyorum (v4.0.30319), ancak TypeSet ve benim için çalışan tüm Süreç yöntemlerinde bu döngüye sahibim. – TechLiam

+0

Oh Ayrıca, onlar için oluşturulmuş örneklere sahip olmayan IRepository gibi jenerik sınıflar için de hatalar yapıyorum, ancak bu kodu gerçekten kaydetmek istemediğimden başkaları da bunu düzeltmek için bir yol bilmeleri gerekebiliyor. – TechLiam