配置ABP
配置是通過在自己模塊的PreInitialize方法中來實現(xiàn)的
代碼示例如下:
public class SimpleTaskSystemModule : AbpModule
{
public override void PreInitialize()
{
//在你的應用中添加語言包,這個是英語和作者的土耳其語。
Configuration.Localization.Languages.Add(new LanguageInfo("en", "English", "famfamfam-flag-england", true));
Configuration.Localization.Languages.Add(new LanguageInfo("tr", "Türkçe", "famfamfam-flag-tr"));
Configuration.Localization.Sources.Add(
new XmlLocalizationSource(
"SimpleTaskSystem",
HttpContext.Current.Server.MapPath("~/Localization/SimpleTaskSystem")
)
);
//配置導航和菜單
Configuration.Navigation.Providers.AddSimpleTaskSystemNavigationProvider>();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
和orchard類似,abp框架一開始就被設計成模塊化的,不同的模塊可以通過abp框架來進行配置。舉個例子吧,不同的模塊都可以添加導航,通過導航添加菜單項到自己定義的主菜單,具體的細節(jié)大家可以參照:
本地化:http://www.aspnetboilerplate.com/Pages/Documents/Localization
導航:http://www.aspnetboilerplate.com/Pages/Documents/Navigation
配置模塊
和.net框架原生的啟動配置相比較,abp有哪些不一樣呢?abp框架的模塊可以通過IAbpModuleConfigurations接口進行個性化的擴展,這樣的話,模塊配置更加簡單、方便。
示例代碼如下:
...
using Abp.Web.Configuration;
...
public override void PreInitialize()
{
Configuration.Modules.AbpWeb().SendAllExceptionsToClients = true;
}
...
在上面這個例子中,我們通過配置AbpWeb模塊,發(fā)送異常到客戶端。當然了,不是每一個模塊都需要這種配置,通常情況下我們需要,是當一個模塊需要在多個不同的應用中重復使用,我們才進行這樣的配置。
為一個模塊創(chuàng)建配置
如下代碼,假如我們有一個命名為MyModule的模塊,并且這各模塊有一些自己的配置。那么我們首先要創(chuàng)建一些類,這些類定義為屬性(譯者注:屬性有自動的get和set訪問器。),代表了不同的配置。
public class MyModuleConfig
{
public bool SampleConfig1 { get; set; }
public string SampleConfig2 { get; set; }
}
接下來,我們通過依賴注入,注冊這個類。
IocManager.RegisterMyModuleConfig>(); //譯者注:在IocManager中注冊了一個類,換句話說,我們通過IocManager可以得到這個類MyModuleConfig的實例。至于IOC的原理這里就不在詳細說了,總之,就是可以得到一個類的實例。
最后,我們通過創(chuàng)建一個擴展的方法IModuleConfigurations來得到配置的引用。如下代碼:
譯者注:模塊配置是一個靜態(tài)類,因為我們需要重復使用它。靜態(tài)方法Mymodule返回的是一個配置接口,參數(shù)是ImoduleConfigurations接口。
現(xiàn)在,在其他模塊中也可以配置我們自定義的這個MyModule模塊了。
Configuration.Modules.MyModule().SampleConfig1 = false;
Configuration.Modules.MyModule().SampleConfig2 = "test";
在某種意義上,MyModule需要這些配置,你能注射MyModuleConfig并且可以使用這些值。
public class MyService : ITransientDependency
{
private readonly MyModuleConfig _configuration;
public MyService(MyModuleConfig configuration)
{
_configuration = configuration;
}
public void DoIt()
{
if (_configuration.SampleConfig2 == "test")
{
//...
}
}
}
這意味著,在abp框架的系統(tǒng)中,所有的模塊都可以集中配置。
ABP依賴注入
什么是依賴注入
如果你已經(jīng)知道依賴注入的概念,構(gòu)造函數(shù)和屬性注入模式,你可以跳過這一節(jié)。
維基百科說:“依賴注入是一種軟件設計模式的一個或多個依賴項注入(或服務),或通過引用傳遞,為依賴對象(或客戶)和客戶端狀態(tài)的一部分。模式之間建立一個客戶的依賴關(guān)系的行為,它允許程序設計是松散耦合的,依賴倒置和單一職責原則。它直接對比service locator模式,它允許客戶了解他們所使用的系統(tǒng)找到依賴。”。
如果不使用依賴注入技術(shù),很難進行依賴管理、模塊化開發(fā)和應用程序模塊化。
傳統(tǒng)方式的問題
在一個應用程序中,類之間相互依賴。假設我們有一個應用程序服務,使用倉儲(repository)類插入實體到數(shù)據(jù)庫。在這種情況下,應用程序服務類依賴于倉儲(repository)類??聪吕?
public class PersonAppService
{
private IPersonRepository _personRepository;
public PersonAppService()
{
_personRepository = new PersonRepository();
}
public void CreatePerson(string name, int age)
{
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
}
}
PersonAppService使用PersonRepository插入Person到數(shù)據(jù)庫。這段代碼的問題:
PersonAppService通過IPersonRepository調(diào)用CreatePerson方法,所以這方法依賴于IPersonRepository,代替了PersonRepository具體類。但PersonAppService(的構(gòu)造函數(shù))仍然依賴于PersonRepository。組件應該依賴于接口而不是實現(xiàn)。這就是所謂的依賴性倒置原則。
如果PersonAppService創(chuàng)建PersonRepository本身,它成為依賴IPersonRepository接口的具體實現(xiàn),不能使用另一個實現(xiàn)。因此,此方式的將接口與實現(xiàn)分離變得毫無意義。硬依賴(hard-dependency)使得代碼緊密耦合和較低的可重用。
我們可能需要在未來改變創(chuàng)建PersonRepository的方式。即,我們可能想讓它創(chuàng)建為單例(單一共享實例而不是為每個使用創(chuàng)建一個對象)?;蛘呶覀兛赡芟胍獎?chuàng)建多個類實現(xiàn)IPersonRepository并根據(jù)條件創(chuàng)建對象。在這種情況下,我們需要修改所有依賴于IPersonRepository的類。
有了這樣的依賴,很難(或不可能)對PersonAppService進行單元測試。
為了克服這些問題,可以使用工廠模式。因此,創(chuàng)建的倉儲類是抽象的。看下面的代碼:
public class PersonAppService
{
private IPersonRepository _personRepository;
public PersonAppService()
{
_personRepository = PersonRepositoryFactory.Create();
}
public void CreatePerson(string name, int age)
{
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
}
}
PersonRepositoryFactory是一個靜態(tài)類,創(chuàng)建并返回一個IPersonRepository。這就是所謂的服務定位器模式。以上依賴問題得到解決,因為PersonAppService不需要創(chuàng)建一個IPersonRepository的實現(xiàn)的對象,這個對象取決于PersonRepositoryFactory的Create方法。但是,仍然存在一些問題:
此時,PersonAppService取決于PersonRepositoryFactory。這是更容易接受,但仍有一個硬依賴(hard-dependency)。
為每個庫或每個依賴項乏味的寫一個工廠類/方法。
測試性依然不好,由于很難使得PersonAppService使用mock實現(xiàn)IPersonRepository。
解決方案:
有一些最佳實踐(模式)用于類依賴。
構(gòu)造函數(shù)注入
重寫上面的例子,如下所示:
public class PersonAppService
{
private IPersonRepository _personRepository;
public PersonAppService(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
public void CreatePerson(string name, int age)
{
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
}
}
這被稱為構(gòu)造函數(shù)注入?,F(xiàn)在,PersonAppService不知道哪些類實現(xiàn)IPersonRepository以及如何創(chuàng)建它。誰需要使用PersonAppService,首先創(chuàng)建一個IPersonRepository PersonAppService并將其傳遞給構(gòu)造函數(shù),如下所示:
var repository = new PersonRepository();
var personService = new PersonAppService(repository);
personService.CreatePerson("Yunus Emre", 19);
構(gòu)造函數(shù)注入是一個完美的方法,使一個類獨立創(chuàng)建依賴對象。但是,上面的代碼有一些問題:
創(chuàng)建一個PersonAppService變得困難。想想如果它有4個依賴,我們必須創(chuàng)建這四個依賴對象,并將它們傳遞到構(gòu)造函數(shù)PersonAppService。
從屬類可能有其他依賴項(在這里,PersonRepository可能有依賴關(guān)系)。所以,我們必須創(chuàng)建PersonAppService的所有依賴項,所有依賴項的依賴關(guān)系等等. .如此,依賴關(guān)系使得我們創(chuàng)建一個對象變得過于復雜了。
幸運的是,依賴注入框架自動化管理依賴關(guān)系。
屬性注入
構(gòu)造函數(shù)注入模式是一個完美的提供類的依賴關(guān)系的方式。通過這種方式,您不能創(chuàng)建類的實例,而不提供依賴項。它也是一個強大的方式顯式地聲明是什么類的需求正確地工作。
但是,在某些情況下,該類依賴于另一個類,但也可以沒有它。這通常是適用于橫切關(guān)注點(如日志記錄)。一個類可以沒有工作日志,但它可以寫日志如果你提供一個日志對象。在這種情況下,您可以定義依賴為公共屬性,而不是讓他們放在構(gòu)造函數(shù)。想想,如果我們想在PersonAppService寫日志。我們可以重寫類如下:
public class PersonAppService
{
public ILogger Logger { get; set; }
private IPersonRepository _personRepository;
public PersonAppService(IPersonRepository personRepository)
{
_personRepository = personRepository;
Logger = NullLogger.Instance;
}
public void CreatePerson(string name, int age)
{
Logger.Debug("Inserting a new person to database with name = " + name);
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
Logger.Debug("Successfully inserted!");
}
}
NullLogger.Instance 是一個單例對象,實現(xiàn)了ILogger接口,但實際上什么都沒做(不寫日志。它實現(xiàn)了ILogger實例,且方法體為空)。現(xiàn)在,PersonAppService可以寫日志了,如果你為PersonAppService實例設置了Logger,如下面:
var personService = new PersonAppService(new PersonRepository());
personService.Logger = new Log4NetLogger();
personService.CreatePerson("Yunus Emre", 19);
假設Log4NetLogger實現(xiàn)ILogger實例,使得我們可以使用Log4Net庫寫日志。因此,PersonAppService可以寫日志。如果我們不設置Logger,PersonAppService就不寫日志。因此,我們可以說PersonAppService ILogger實例是一個可選的依賴。
幾乎所有的依賴注入框架都支持屬性注入模式
依賴注入框架
有許多依賴注入框架,都可以自動解決依賴關(guān)系。他們可以創(chuàng)建所有依賴項(遞歸地依賴和依賴關(guān)系)。所以你只需要根據(jù)注入模式寫類和類構(gòu)造函數(shù)屬性,其他的交給DI框架處理!在良好的應用程序中,類甚至獨立于DI框架。整個應用程序只會有幾行代碼或類,顯示的與DI框架交互。
ABP的依賴注入基于 Castle Windsor框架。Castle Windsor最成熟的DI框架之一。還有很多這樣的框架,如Unity,Ninject,StructureMap,Autofac等等。
在使用一個依賴注入框架時,首先注冊您的接口/類到依賴注入框架中,然后你就可以resolve一個對象。在Castle Windsor,它是這樣的:
var container = new WindsorContainer();
container.Register(
Component.ForIPersonRepository>().ImplementedByPersonRepository>().LifestyleTransient(),
Component.ForIPersonAppService>().ImplementedByPersonAppService>().LifestyleTransient()
);
var personService = container.ResolveIPersonAppService>();
personService.CreatePerson("Yunus Emre", 19);
我們首先創(chuàng)建了WindsorContainer。然后注冊PersonRepository 和 PersonAppService及它們的接口。然后我們要求容器創(chuàng)建一個IPersonAppService實例。它創(chuàng)建PersonAppService對象及其依賴項并返回。在這個簡單的示例中,使用DI框架也許不是那么簡潔,但想象下,在實際的企業(yè)應用程序中你會有很多類和依賴關(guān)系。當然,注冊的依賴項只在程序啟動的某個地方創(chuàng)建一次。
請注意,我們只是講對象聲明為臨時對象(transient)。這意味著每當我們創(chuàng)建這些類型的一個對象時,就會創(chuàng)建一個新的實例。有許多不同的生命周期(如Singletion)。
ABP依賴注入的基礎結(jié)構(gòu)
在編寫應用程序時遵循最佳實踐和一些約定,ABP幾乎讓依賴注入框架使用變得無形。
注冊:
在ABP中,有很多種不同的方法來注冊你的類到依賴注入系統(tǒng)。大部分時間,常規(guī)方法就足夠了。
常規(guī)注冊:
按照約定,ABP自動注冊所有 Repositories, Domain Services, Application Services, MVC 控制器和Web API控制器。例如,您可能有一個IPersonAppService 接口和實現(xiàn)類PersonAppService:
public interface IPersonAppService : IApplicationService
{
//...
}
public class PersonAppService : IPersonAppService
{
//...
}
ABP會自動注冊它,因為它實現(xiàn)IApplicationService接口(它只是一個空的接口)。它會被注冊為transient (每次使用都創(chuàng)建實例)。當你注入(使用構(gòu)造函數(shù)注入)IPersonAppService接口成一個類,PersonAppService對象會被自動創(chuàng)建并傳遞給構(gòu)造函數(shù)。
命名約定在這里非常重要。例如你可以將名字PersonAppService改為 MyPersonAppService或另一個包含“PersonAppService”后綴的名稱,由于IPersonAppService包含這個后綴。但是你可以不遵循PeopleService命名您的服務類。如果你這樣做,它將不會為IPersonAppService自動注冊(它需要自注冊(self-registration)到DI框架,而不是接口),所以,如果你想要你應該手動注冊它。
ABP按照約定注冊程序集。所以,你應該告訴ABP按照約定注冊您的程序集。這很容易:
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
Assembly.GetExecutingAssembly()得到一個對包括此代碼的程序集的引用。你可以通過RegisterAssemblyByConvention方法注冊其他程序集。這同在你的模塊初始化(AbpModule.Initialize())時完成。請查看ABP的模塊系統(tǒng)獲得更多信息。
您可以通過實現(xiàn)IConventionalRegisterer接口和調(diào)用IocManager。AddConventionalRegisterer方法編寫自己的約定注冊類。你應該將它添加到模塊的pre-initialize方法中。
幫助接口
你可以注冊一個特定的類,不遵循傳統(tǒng)的約定制度規(guī)則。ABP提供了ITransientDependency和ISingletonDependency接口的快捷方法。例如:
public interface IPersonManager
{
//...
}
public class MyPersonManager : IPersonManager, ISingletonDependency
{
//...
}
以這種方式,您可以很容易地注冊MyPersonManager為transient。當需要注入IPersonManager時,MyPersonManager會被使用。注意,依賴被聲明為單例。因此,創(chuàng)建的MyPersonManager同一個對象被傳遞給所有需要的類。只是在第一次使用時創(chuàng)建,那么應用程序的整生命周期使用的是同一實例。
自定義/直接 注冊
如果之前描述的方法還是不足以應對你的情況,你可以使用Castle Windsor注冊類和及依賴項。因此,您將擁有Castle Windsor注冊的所有能力。
可以實現(xiàn)IWindsorInstaller接口進行注冊。您可以在應用程序中創(chuàng)建一個實現(xiàn)IWindsorInstaller接口的類:
public class MyInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOnIMySpecialInterface>().LifestylePerThread().WithServiceSelf());
}
}
Abp自動發(fā)現(xiàn)和執(zhí)行這個類。最后,你可以通過使用IIocManager.IocContainer屬性得到WindsorContainer。有關(guān)更多信息,閱讀Windsor的文檔。
解析(Resolving)
注冊通知IOC(控制反轉(zhuǎn))容器關(guān)于你的類,它們的依賴項和生命周期。在您的應用程序需要使用IOC容器創(chuàng)建對象時,ASP.NET提供了一些方法解決依賴關(guān)系。
構(gòu)造函數(shù) 屬性注入
作為最佳實踐,你可以使用構(gòu)造函數(shù)和屬性注入去獲取你的類的依賴。任何可能的地方,你都應該這樣做。例子:
public class PersonAppService
{
public ILogger Logger { get; set; }
private IPersonRepository _personRepository;
public PersonAppService(IPersonRepository personRepository)
{
_personRepository = personRepository;
Logger = NullLogger.Instance;
}
public void CreatePerson(string name, int age)
{
Logger.Debug("Inserting a new person to database with name = " + name);
var person = new Person { Name = name, Age = age };
_personRepository.Insert(person);
Logger.Debug("Successfully inserted!");
}
}
IPersonRepository從構(gòu)造函數(shù)注入,ILogger實例從公共屬性注入。這樣,您的代碼不會體現(xiàn)依賴注入系統(tǒng)。這是使用DI系統(tǒng)最適當?shù)姆绞健?/p>
IIocResolver 和 IIocManager
有時你可能需要直接創(chuàng)建你的依賴項,而不是構(gòu)造函數(shù)和屬性注入。應該盡可能避免這種情況,但它可能無法避免。Abp提供一些服務使得這樣的注入很容易實現(xiàn)。例子:
public class MySampleClass : ITransientDependency
{
private readonly IIocResolver _iocResolver;
public MySampleClass(IIocResolver iocResolver)
{
_iocResolver = iocResolver;
}
public void DoIt()
{
//Resolving, using and releasing manually
var personService1 = _iocResolver.ResolvePersonAppService>();
personService1.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
_iocResolver.Release(personService1);
//Resolving and using in a safe way
using (var personService2 = _iocResolver.ResolveAsDisposablePersonAppService>())
{
personService2.Object.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
}
}
}
MySampleClass是一個應用程序的示例類。IIcResolver通過構(gòu)造函數(shù)注入,然后用它來創(chuàng)建和釋放對象。有幾個解決方法的重載可以根據(jù)需要使用。Release方法用于釋放組件(對象)。如果你是手動創(chuàng)建一個對象,調(diào)用Release方法釋放對象非常重要。否則,您的應用程序會有內(nèi)存泄漏問題。為了保證對象被釋放,盡可能使用ResolveAsDisposable(就像上面的例子所示)。它會在using代碼塊結(jié)束的時候自動調(diào)用Release方法。
如果你想直接使用IOC容器(Castle Windsor)來處理依賴關(guān)系項,可以通過構(gòu)造函數(shù)注入 IIocManager并使用它IIocManager.IocContainer 屬性。如果你是在一個靜態(tài)上下文或不能注入IIocManager,還有最后一個方法,你可以使用單例對象IocManager.Instance,你可以在任何地方獲取到,它無處不在。但是,在這種情況下你的代碼將變得不易容測試。
附加
IShouldInitialize 接口:
有些類在第一次使用前需要初始化。IShouldInitialize有Initialize()方法。如果你實現(xiàn)它,那么你的Initialize()方法自動會被自動調(diào)用在創(chuàng)建對象之后(在使用之前)。當然,為了使用這個特性,你應該注入/創(chuàng)建此對象。
ASP.NET MVC ASP.NET Web API 集成:
當然,我們必須調(diào)用依賴注入系統(tǒng)處理依賴關(guān)系圖的根對象。在一個ASP.NET MVC應用程序,通常是一個控制器類。我們可以使用構(gòu)造函數(shù)注入模式注入控制器。當一個請求來到我們的應用程序中,控制器和所有依賴項被IOC容器遞歸創(chuàng)建。所以,誰做了這些?這是被Abp擴展的ASP.NET MVC默認控制器工廠自動完成的。ASP.NET Web API 也是相似的。你不用關(guān)心對象的創(chuàng)建和釋放。
您可能感興趣的文章:- jQuery.ajax 跨域請求webapi設置headers的解決方案
- WebApi+Bootstrap+KnockoutJs打造單頁面程序
- C#進階系列 WebApi身份認證解決方案推薦:Basic基礎認證
- ASP.net WebAPI 上傳圖片實例
- AngularJS $injector 依賴注入詳解
- 解讀ASP.NET 5 & MVC6系列教程(7):依賴注入
- 詳解Java Spring各種依賴注入注解的區(qū)別
- Javascript技術(shù)棧中的四種依賴注入詳解
- AngularJs動態(tài)加載模塊和依賴注入詳解
- webapi中如何使用依賴注入