下載自:https://github.com/aspnetboilerplate/module-zero
打開D:\ABP\module-zero-master\sample里的ModuleZeroSampleProject.sln項目文件,馬上還原程序包,試圖update-database,報錯,說沒這個命令。
網上搜了半天,才看到解決方法:
輸入set-executionpolicy remotesigned

再輸入:
PM> Import-Module D:\ABP\module-zero-master\sample\packages\EntityFramework.6.1.3\tools\EntityFramework.psd1
PM> update-database
終於搞定。
(奇怪的是,重新解壓,直接編譯,讓VS自動還原程序包,就不會出現上面的問題,直接update-database通過)
還有就是別手癢,去更新程序包,我下載的項目里還使用jquery-2.1.3.min.js,一更新就被換成jquery-2.1.4.min.js,但是BundleConfig類里還是2.1.3,沒有改2.1.4啊…………
啟動ModuleZeroSampleProject.Web項目,使用admin/123qwe登錄沒有問題。但是如果無輸入、亂輸入,則沒有任何錯誤提示。
參考http://qasample.aspnetboilerplate.com/示例網站,分明是有錯誤提示的嘛:
原來還得改Web.config:
<customErrors mode="On" />
一個典型的ABP項目:

先看Core項目下的實體:

用戶:
User繼承的是AbpUser<Tenant, User>
UserManager繼承的是AbpUserManager<Tenant, Role, User>
UserStore繼承的是AbpUserStore<Tenant, Role, User>
這幾個類實際上並無增加內容,僅僅是直接繼承而已。需要注意的是User所繼承的AbpUser<Tenant, User>使用了子類User作為TUser
角色:
Role繼承的是AbpRole<Tenant, User>
RoleStore繼承的是AbpRoleStore<Tenant, Role, User>
RoleManager繼承的是AbpRoleManager<Tenant, Role, User>
PermissionChecker繼承的是PermissionChecker<Tenant, Role, User>
同樣,這些子類都沒有增加內容。
租戶:
Tenant繼承的是AbpTenant<Tenant, User>
問題和回答:
Question繼承了CreationAuditedEntity<int, User>,表明這個類要求包含創建者信息,后面可以看到需要使用到它的CreatorUser屬性。
在數據表Questions里能夠發現:

這個字段並沒有在Question類里出現,要在CreationAuditedEntity里找:

因為問題和回答是一對多關系,所以有屬性:
public virtual ICollection<Answer> Answers { get; set; }
Answer也繼承了CreationAuditedEntity<int, User>
注意到有屬性IsAccepted
領域服務:QuestionDomainService,繼承了DomainService類
這個服務就是放“接受答案”方法的地方:

就是如果已有接受的答案,則找出來,設置為非接受,然后設置傳入的答案為已接受。
注意在這個類已經使用了IRepository<Answer>
再看ModuleZeroSampleProject.EntityFramework項目下的ModuleZeroSampleProjectDbContext類
繼承的是AbpZeroDbContext<Tenant, Role, User>
聲明了:
public virtual IDbSet<Question> Questions { get; set; }
public virtual IDbSet<Answer> Answers { get; set; }
但是查看數據庫,數據表可不止這兩個。

再看ModuleZeroSampleProject.Application項目:
ModuleZeroSampleProjectAuthorizationProvider類,繼承AuthorizationProvider,聲明了幾個權限:

創建問題、刪除問題、刪除答案、回答問題。
設置類MySettingProvider,繼承SettingProvider,設置了一頁顯示10條問題。
IUserAppService聲明了GetUsers方法,也就是用戶列表。
IQuestionAppService聲明了
GetQuestions獲得所有問題
CreateQuestion創建問題
GetQuestion獲取一個問題
VoteUp加1投票
VoteDown減1投票
SubmitAnswer回答問題
AcceptAnswer接受答案
再看實現:QuestionAppService類
GetQuestions方法中,讀取了MySettingProvider里的配置:
input.MaxResultCount = SettingManager.GetSettingValue<int>(MySettingProvider.QuestionsDefaultPageSize);
獲取問題列表,需要注意輸入的參數為GetQuestionsInput對象,該對象實現了IPagedResultRequest、ISortedResultRequest
看表達式

需要知道,Question類繼承了CreationAuditedEntity<int, User>,因而具有CreateUser屬性。
這里使用了EF里擴展的Include方法,要求根據外鍵CreatorUserID來加載創建者
PageBy方法是ABP擴展的一個分頁方法。
另外要注意的是automapping:
[AutoMapFrom(typeof(Question))]
public class QuestionDto : CreationAuditedEntityDto
QuestionDto 類只有CreatorUserName屬性,而沒有CreatorUserID屬性,但是還能映射:
Items = questions.MapTo<List<QuestionDto>>()
CreateQuestion方法:
可以看到CreateQuestion方法前,加了[AbpAuthorize("CanCreateQuestions")] 特性,對權限的驗證非常便捷。
異步插入數據的寫法:

GetQuestion方法:
跨表查詢好狠:
var question =
_questionRepository
.GetAll()
.Include(q => q.CreatorUser)
.Include(q => q.Answers)
.Include("Answers.CreatorUser")
.FirstOrDefault(q => q.Id == input.Id);
SubmitAnswer方法:
使用了 _unitOfWorkManager.Current.SaveChanges();
AcceptAnswer方法:
直接調用了領域服務:_questionDomainService.AcceptAnswer(answer)
再看Tests項目對上面服務方法的測試,首先進行注入:
聲明一個繼承於AbpIntegratedTestBase的類SampleProjectTestBase,其構造函數

注意使用了Effort組件。詳情可以看文章
http://www.codeproject.com/Articles/871786/Unit-testing-in-Csharp-using-xUnit-Entity-Framewor
具體的測試類如UserAppService_Tests,需要繼承SampleProjectTestBase
重新生成,就可以運行測試了:

Web項目,AccountController類,注意到使用了UserManager、AuthenticationManager
通過HttpContext.GetOwinContext().Authentication獲取AuthenticationManager,使用了Owin
UserManager是通過AccountController的構造函數傳入的,前者的父類有實現ITransientDependency,所以這里會被自動注入。
使用:
var loginResult = await _userManager.LoginAsync(
var loginResult = await _userManager.LoginAsync(
loginModel.UsernameOrEmailAddress,
loginModel.Password,
loginModel.TenancyName
);
LoginAsync方法:
public virtual Task<AbpUserManager<TTenant, TRole, TUser>.AbpLoginResult> LoginAsync(string userNameOrEmailAddress, string plainPassword, string tenancyName = null)
這方法需要去zero的源碼里研究
AuthenticationManager可直接使用其SignIn、SignOut方法:
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = loginModel.RememberMe }, loginResult.Identity);
HomeController加了 [AbpMvcAuthorize]特性。
Startup類可以看到配置,[assembly: OwinStartup(typeof(Startup))]
在Configuration方法里
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
詳情參考文章
Code! MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on