Castle 多繼承選擇

二胡嘈子發表於2016-03-11

Castle 多繼承選擇

很多時候,我們定義了一個介面,但是這個介面會有多種不同的,這時IOC建構函式注入的時候,就需要自動選擇對應的實現。

public interface ITestService {}

public class TestService : IService
{
     public string test()
     {
         return "test"
     }
}

public class TestService_New : ICalculatorService
{
     public string test()
     {
         return "test new";
     }
}

此時,我們大多數情況下,需要用TestService來實現ITestService,但是少部分情況下,需要用TestService_New實現ITestService。

解決方案一

IOC中只實現需要用到的類:

Component.For<ITestService>()
    .ImplementedBy<TestService>() 

如果在某些地方部署時,修改為
Component.For()
.ImplementedBy()

這樣每次部署的時候都需要修改程式碼,而且需要手寫的規則會越來越多。

解決方案二

參考了Castle的WIKI,發現了可以ServiceOverrides重寫實現:

 Component.For<ITestService>()
    .ImplementedBy<TestService>() 
    .Named("myservice.default"),
Component.For<ITestService>()
    .ImplementedBy<TestService_New>()
    .Named("myservice.new"),

Component.For<TestController>()
    .ServiceOverrides(ServiceOverride.ForKey("myService").Eq("myservice.new"))

嘗試了一下,VS提示改方法已過世,建議使用Dependency.OnComponent代替。
Component.For().
.DependsOn(Dependency.OnComponent<ITestService, TestService_New>())

修改程式碼之後,怎麼都不起作用。重新翻看官方WIKI,發現了IsDefault方法,當多個類實現同一個介面時,可以通過IsDefault來置為預設實現。

解決方案三

             //先通過DefaultInterfaces載入正常實現 
             Classes.FromAssembly(assembly)
             .IncludeNonPublicTypes()
             .BasedOn<IApplicationService>()
             .WithService.DefaultInterfaces()
             .LifestyleTransient()
             .Configure(c =>
             {
                 c.Interceptors<ExceptionInterceptor, TransactionInterceptor>();
                 c.Named(c.Implementation.Name);
             }),
             //根據WEBCONFIG載入特殊實現,並設定為預設實現
             Classes.FromAssembly(assembly)
            .IncludeNonPublicTypes()
            .BasedOn<IApplicationService>()
            .WithService.Select((type, @base) =>
                      type.GetAllInterfaces()
                          .Where(i => type.Name.Contains(GetInterfaceNameFromConf(i))))
            .LifestyleTransient()
            .Configure(c =>
            {
                c.Interceptors<ExceptionInterceptor, TransactionInterceptor>().IsDefault()
                .Named(c.Implementation.FullName +  "_" + ConfigurationManager.AppSettings["City"]);
            })


            private string GetInterfaceNameFromConf(Type @interface)
            {
                var name = @interface.Name;
                if ((name.Length > 1 && name[0] == 'I') && char.IsUpper(name[1]))
                {
                    return name.Substring(1) + "_" + ConfigurationManager.AppSettings["City"];
                }
                return name;
            }

OK,大功告成。

相關文章