代碼已上傳Github+Gitee德澈,文末有地址
番外:時間真快,今天終于到了系統(tǒng)打包的日子固惯,雖然項目還是有很多問題梆造,雖然后邊還有很多的內(nèi)容要說要學(xué),但是想著初級基本的.Net Core 用到的基本至少就這么多了(接口文檔缝呕,項目框架澳窑,持久化ORM,依賴注入供常,AOP摊聋,分布式緩存,CORS跨域等等)栈暇,中高級的麻裁,比如在Linux高級發(fā)布,Nginx代理源祈,微服務(wù)煎源,Dockers等等,這個在以后的更新中會慢慢提到香缺,不然的話手销,Vue就一直說不到了 [哭笑哈哈],其實我還有很多要總結(jié)的图张,比如 Power BI系列(沒用過的點擊看看)锋拖,比如C#7.0系列等文章,都在慢慢醞釀中祸轮,希望能堅持下來兽埃,不過這兩個系列目前還不會寫到,如果有需要用或?qū)W微軟Power BI(https://docs.microsoft.com/zh-cn/power-bi/sample-customer-profitability)的适袜,可以加QQ群聯(lián)系我柄错,我在微軟項目中已經(jīng)用到了。還是打算從下周一開始轉(zhuǎn)戰(zhàn)Vue的文章苦酱,當然后端也會一直穿插著售貌,這里要說下,我們的QQ群已經(jīng)有一些小伙伴了疫萤,每天可以一起交流心得和問題趁矾,感覺還是很不錯的,如果你有什么問題给僵,或者其他技術(shù)上的需要討論,咱們的群是可以試試喲,我和其他小伙伴會一直在線給大家解答(咋感覺像一個廣告哈哈帝际,大家隨意哈)蔓同。
正傳:好啦,書接上文蹲诀,昨天說到了《從壹開始前后端分離【 .NET Core2.0 +Vue2.0 】框架之十二 || 三種跨域方式比較斑粱,DTOs(數(shù)據(jù)傳輸對象)初探》,因為下午時間的問題脯爪,只是講解了四種跨域方法则北,沒有講解完DTO,其實這個東西很簡單痕慢,說白了尚揣,就是把兩個實體類進行轉(zhuǎn)換,不用人工手動去一一賦值掖举,今天呢快骗,就簡單說下常見DTO框架AutoMapper的使用,然后做一個打包處理塔次,發(fā)布到我的windows服務(wù)器里方篮,今天剛剛買了一個Ubuntu Linux服務(wù)器,因為如果開發(fā).Net Core励负,一定會接觸到Linux服務(wù)器藕溅,等各種,因為它跨域了继榆,就是醬紫巾表。但是還沒有配置好,所以會在下邊留下位置裕照,慢慢補充在Ubuntu部署的講解攒发。
零、今天完成右下角的深藍色部分
一晋南、在項目中使用添加一個案例使用AutoMapper
1惠猿、在接口 IBlogArticleServices.cs和 類BlogArticleServices.cs中,添加GetBlogDetails()方法负间,返回類型是BlogViewModels
請看這兩個類
/// <summary>
/// 博客文章實體類 /// </summary>
public class BlogArticle
{ /// <summary>
///
/// </summary>
public int bID { get; set; } /// <summary>
/// 創(chuàng)建人 /// </summary>
public string bsubmitter { get; set; } /// <summary>
/// 博客標題 /// </summary>
public string btitle { get; set; } /// <summary>
/// 類別 /// </summary>
public string bcategory { get; set; } /// <summary>
/// 內(nèi)容 /// </summary>
public string bcontent { get; set; } /// <summary>
/// 訪問量 /// </summary>
public int btraffic { get; set; } /// <summary>
/// 評論數(shù)量 /// </summary>
public int bcommentNum { get; set; } /// <summary>
/// 修改時間 /// </summary>
public DateTime bUpdateTime { get; set; } /// <summary>
/// 創(chuàng)建時間 /// </summary>
public System.DateTime bCreateTime { get; set; } /// <summary>
/// 備注 /// </summary>
public string bRemark { get; set; }
} -------------------------------------------------
/// <summary>
/// 博客信息展示類 /// </summary>
public class BlogViewModels
{ /// <summary>
///
/// </summary>
public int bID { get; set; } /// <summary>/// 創(chuàng)建人
/// </summary>
public string bsubmitter { get; set; } /// <summary>/// 博客標題
/// </summary>
public string btitle { get; set; } /// <summary>/// 摘要
/// </summary>
public string digest { get; set; } /// <summary>
/// 上一篇 /// </summary>
public string previous { get; set; } /// <summary>
/// 上一篇id /// </summary>
public int previousID { get; set; } /// <summary>
/// 下一篇 /// </summary>
public string next { get; set; } /// <summary>
/// 下一篇id /// </summary>
public int nextID { get; set; } /// <summary>/// 類別
/// </summary>
public string bcategory { get; set; } /// <summary>/// 內(nèi)容
/// </summary>
public string bcontent { get; set; } /// <summary>
/// 訪問量 /// </summary>
public int btraffic { get; set; } /// <summary>
/// 評論數(shù)量 /// </summary>
public int bcommentNum { get; set; } /// <summary>/// 修改時間
/// </summary>
public DateTime bUpdateTime { get; set; } /// <summary>
/// 創(chuàng)建時間 /// </summary>
public System.DateTime bCreateTime { get; set; } /// <summary>/// 備注
/// </summary>
public string bRemark { get; set; }
}
兩個實體類字段還基本可以偶妖,不是很多,但是我曾經(jīng)開發(fā)一個旅游網(wǎng)站的系統(tǒng)政溃,有一個表字段都高達30多個趾访,當然還有更多的,額董虱,如果我們一個個賦值是這樣的
BlogViewModels models = new BlogViewModels()
{
bsubmitter=blogArticle.bsubmitter,
btitle = blogArticle.btitle,
bcategory = blogArticle.bcategory,
bcontent = blogArticle.bcontent,
btraffic = blogArticle.btraffic,
bcommentNum = blogArticle.bcommentNum,
bUpdateTime = blogArticle.bUpdateTime,
bCreateTime = blogArticle.bCreateTime,
bRemark = blogArticle.bRemark,
};
所以這個方法的全部代碼是:
接口層也要添加:
public interface IBlogArticleServices :IBaseServices<BlogArticle> {
Task<List<BlogArticle>> getBlogs();
Task<BlogViewModels> getBlogDetails(int id);
}
/// <summary>
/// 獲取視圖博客詳情信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<BlogViewModels> getBlogDetails(int id)
{
var bloglist = await dal.Query(a => a.bID > 0, a => a.bID);
var idmin = bloglist.FirstOrDefault() != null ? bloglist.FirstOrDefault().bID : 0;
var idmax = bloglist.LastOrDefault() != null ? bloglist.LastOrDefault().bID : 1;
var idminshow = id;
var idmaxshow = id;
BlogArticle blogArticle = new BlogArticle();
blogArticle = (await dal.Query(a => a.bID == idminshow)).FirstOrDefault();
BlogArticle prevblog = new BlogArticle();
while (idminshow > idmin)
{
idminshow--;
prevblog = (await dal.Query(a => a.bID == idminshow)).FirstOrDefault();
if (prevblog != null)
{
break;
}
}
BlogArticle nextblog = new BlogArticle();
while (idmaxshow < idmax)
{
idmaxshow++;
nextblog = (await dal.Query(a => a.bID == idmaxshow)).FirstOrDefault();
if (nextblog != null)
{
break;
}
}
blogArticle.btraffic += 1;
await dal.Update(blogArticle, new List<string> { "btraffic" });
BlogViewModels models = new BlogViewModels()
{
bsubmitter=blogArticle.bsubmitter,
btitle = blogArticle.btitle,
bcategory = blogArticle.bcategory,
bcontent = blogArticle.bcontent,
btraffic = blogArticle.btraffic,
bcommentNum = blogArticle.bcommentNum,
bUpdateTime = blogArticle.bUpdateTime,
bCreateTime = blogArticle.bCreateTime,
bRemark = blogArticle.bRemark,
};
if (nextblog != null)
{
models.next = nextblog.btitle;
models.nextID = nextblog.bID;
}
if (prevblog != null)
{
models.previous = prevblog.btitle;
models.previousID = prevblog.bID;
}
return models;
}
想了想這才是一個方法扼鞋,一般的系統(tǒng)都會有少則幾十申鱼,多則上百個這樣的方法,這還不算云头,大家肯定遇到過一個情況捐友,如果有一天要在頁面多顯示一個字段,噗溃槐!不是吧匣砖,首先要存在數(shù)據(jù)庫,然后在該實體類就應(yīng)該多一個昏滴,然后再在每一個賦值的地方增加一個猴鲫,而且也沒有更好的辦法不是,一不小心就少了一個谣殊,然后被產(chǎn)品測試說咱們不細心拂共,心塞喲,別慌蟹倾!神器來了匣缘,一招搞定。
2鲜棠、先來引入DTO講解肌厨,以及它的原理
在學(xué)習(xí)EF的時候我們知道了ORM(Object Relational Mapping)映射,是一種對象關(guān)系的映射豁陆,對象-關(guān)系映射(ORM)系統(tǒng)一般以中間件的形式存在柑爸,主要實現(xiàn)程序?qū)ο蟮疥P(guān)系數(shù)據(jù)庫數(shù)據(jù)的映射。
而Automapper是一種實體轉(zhuǎn)換關(guān)系的模型盒音,AutoMapper是一個.NET的對象映射工具表鳍。主要作用是進行領(lǐng)域?qū)ο笈c模型(DTO)之間的轉(zhuǎn)換、數(shù)據(jù)庫查詢結(jié)果映射至實體對象祥诽。
下邊的是基本原理譬圣,大家喵一眼就行:
? 什么是DTO?
數(shù)據(jù)傳輸對象(DTO)(DataTransfer Object),是一種設(shè)計模式之間傳輸數(shù)據(jù)的軟件應(yīng)用系統(tǒng)雄坪。數(shù)據(jù)傳輸目標往往是數(shù)據(jù)訪問對象從而從數(shù)據(jù)庫中檢索數(shù)據(jù)厘熟。數(shù)據(jù)傳輸對象與數(shù)據(jù)交互對象或數(shù)據(jù)訪問對象之間的差異是一個以不具有任何行為除了存儲和檢索的數(shù)據(jù)(訪問和存取器)。? 為什么用维哈?
它的目的只是為了對領(lǐng)域?qū)ο筮M行數(shù)據(jù)封裝绳姨,實現(xiàn)層與層之間的數(shù)據(jù)傳遞。為何不能直接將領(lǐng)域?qū)ο笥糜跀?shù)據(jù)傳遞阔挠?因為領(lǐng)域?qū)ο蟾⒅仡I(lǐng)域飘庄,而DTO更注重數(shù)據(jù)。不僅如此购撼,由于“富領(lǐng)域模型”的特點跪削,這樣做會直接將領(lǐng)域?qū)ο蟮男袨楸┞督o表現(xiàn)層谴仙。需要了解的是,數(shù)據(jù)傳輸對象DTO本身并不是業(yè)務(wù)對象碾盐。數(shù)據(jù)傳輸對象是根據(jù)UI的需求進行設(shè)計的狞甚,而不是根據(jù)領(lǐng)域?qū)ο筮M行設(shè)計的。比如廓旬,Customer領(lǐng)域?qū)ο罂赡軙恍┲T如FirstName, LastName, Email, Address等信息。但如果UI上不打算顯示Address的信息谐腰,那么CustomerDTO中也無需包含這個 Address的數(shù)據(jù)”孕豹。
? 什么是領(lǐng)域?qū)ο螅?br> 領(lǐng)域模型就是面向?qū)ο蟮模嫦驅(qū)ο蟮囊粋€很重要的點就是:“把事情交給最適合的類去做”十气,即:“你得在一個個領(lǐng)域類之間跳轉(zhuǎn)励背,才能找出他們?nèi)绾谓换ァ薄T谖覀兊南到y(tǒng)中Model(EF中的實體)就是領(lǐng)域模型對象砸西。領(lǐng)域?qū)ο笾饕敲鎸I(yè)務(wù)的叶眉,我們是通過業(yè)務(wù)來定義Model的。
以上的這些大家簡單看看原理即可芹枷,意思大家肯定都懂衅疙,下邊開始講解如何使用
3、在Blog.Core.Services項目中引用Nuget包鸳慈,AutoMapper 和 AutoMapper.Extensions.Microsoft.DependencyInjection
AutoMapper.Extensions.Microsoft.DependencyInjection饱溢,這個是用來配合依賴注入的,看名字也能看的出來吧走芋,大家回憶下绩郎,整個項目中,都是使用的依賴注入翁逞,所以盡量不要用new 來實例化肋杖,導(dǎo)致層耦合。
4挖函、基于上邊原理状植,在接口層Blog.Core 中,添加文件夾AutoMapper挪圾,然后添加映射配置文件 CustomProfile.cs浅萧,用來匹配所有的映射對象關(guān)系
public class CustomProfile : Profile
{ /// <summary>
/// 配置構(gòu)造函數(shù),用來創(chuàng)建關(guān)系映射 /// </summary>
public CustomProfile()
{
CreateMap<BlogArticle, BlogViewModels>();
}
}
大家看下F12這個CreateMap方法
public IMappingExpression<TSource, TDestination> CreateMap<TSource, TDestination>();
第一個參數(shù)是原對象哲思,第二個是目的對象洼畅,所以,要想好棚赔,是哪個方向轉(zhuǎn)哪個帝簇,當然可以都寫上徘郭,比如
CreateMap<BlogArticle, BlogViewModels>();
CreateMap<BlogViewModels, BlogArticle>();
5、修改上邊服務(wù)層BlogArticleServices.cs 中g(shù)etBlogDetails 方法中的賦值丧肴,改用AutoMapper残揉,并用構(gòu)造函數(shù)注入
最終的代碼是
IBlogArticleRepository dal;
IMapper IMapper; public BlogArticleServices(IBlogArticleRepository dal, IMapper IMapper)
{ this.dal = dal; base.baseDal = dal; this.IMapper = IMapper;
} public async Task<BlogViewModels> getBlogDetails(int id)
{ var bloglist = await dal.Query(a => a.bID > 0, a => a.bID); var idmin = bloglist.FirstOrDefault() != null ? bloglist.FirstOrDefault().bID : 0; var idmax = bloglist.LastOrDefault() != null ? bloglist.LastOrDefault().bID : 1; var idminshow = id; var idmaxshow = id;
BlogArticle blogArticle = new BlogArticle();
blogArticle = (await dal.Query(a => a.bID == idminshow)).FirstOrDefault();
BlogArticle prevblog = new BlogArticle(); while (idminshow > idmin)
{
idminshow--;
prevblog = (await dal.Query(a => a.bID == idminshow)).FirstOrDefault(); if (prevblog != null)
{ break;
}
}
BlogArticle nextblog = new BlogArticle(); while (idmaxshow < idmax)
{
idmaxshow++;
nextblog = (await dal.Query(a => a.bID == idmaxshow)).FirstOrDefault(); if (nextblog != null)
{ break;
}
}
blogArticle.btraffic += 1; await dal.Update(blogArticle, new List<string> { "btraffic" }); //注意就是這里
BlogViewModels models = IMapper.Map<BlogViewModels>(blogArticle); if (nextblog != null)
{
models.next = nextblog.btitle;
models.nextID = nextblog.bID;
} if (prevblog != null)
{
models.previous = prevblog.btitle;
models.previousID = prevblog.bID;
} return models;
}
6、老規(guī)矩芋浮,還是在Startup中抱环,注入服務(wù)
services.AddAutoMapper(typeof(Startup));//這是AutoMapper的2.0新特性
7、修改BlogController.cs中的 Get(int id)方法纸巷,運行項目镇草,斷點調(diào)試,發(fā)現(xiàn)已經(jīng)成功了瘤旨,是不是很方便梯啤,你也可以反過來試一試
[HttpGet("{id}", Name = "Get")]
public async Task<object> Get(int id)
{
var model = await blogArticleServices.getBlogDetails(id);//調(diào)用該方法
var data = new { success = true, data = model }; return data;
}
8、好啦存哲,DTOs就到這里了因宇,我們的ConfigureServices也基本告一段落了,主要有這些
二祟偷、Blog.Core項目打包發(fā)布在IIS
1察滑、在項目Blog.Core中,右鍵肩袍,發(fā)布杭棵,選擇文件,相信大家都會氛赐,不會的可以聯(lián)系我
注意: 這里有一個坑魂爪,還記得我們用swagger中使用的兩個xml文件么,編譯的時候有艰管,但是.net core官方限制了在發(fā)布的時候包含xml文件滓侍,所以我們需要處理下
在項目工程文件WebApplication1.csproj中,增加
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile></PropertyGroup>
當然我們還可以基于CLI的Publish命令進行發(fā)布牲芋,只需切換到Light.API根目錄下撩笆,輸入以下命令即可:
dotnet publish --framework netcoreapp1.1 --output "E:\Publish" --configuration Release
framework表示目標框架,output表示要發(fā)布到的目錄文件夾缸浦,configuration表示配置文件夕冲,等同于和上面我們通過管理器來發(fā)布的操作
具體的大家可以自行實驗
2、一定要在服務(wù)器中安裝.Net Core SDK (已安裝則跳過):
地址:https://www.microsoft.com/net/download
在CMD命令窗口下裂逐,輸入 dotnet 查看
3歹鱼、安裝WindowsHosting(已安裝則跳過)
IIS安裝服務(wù)器上安裝DotNetCore.X.X.X-WindowsHosting安裝成功后重啟IIS服務(wù)器。根據(jù)版本選擇下載
下載地址:https://www.microsoft.com/net/download/windows
4卜高、安裝AspNetCoreModule托管模塊(已安裝則跳過),
下載地址:點擊我下載
5弥姻、應(yīng)用池配置為無托管代碼
(網(wǎng)上解釋:ASP.NET Core不再是由IIS工作進程(w3wp.exe)托管南片,而是使用自托管Web服務(wù)器(Kestrel)運行,IIS則是作為反向代理的角色轉(zhuǎn)發(fā)請求到Kestrel不同端口的ASP.NET Core程序中庭敦,隨后就將接收到的請求推送至中間件管道中去疼进,處理完你的請求和相關(guān)業(yè)務(wù)邏輯之后再將HTTP響應(yīng)數(shù)據(jù)重新回寫到IIS中,最終轉(zhuǎn)達到不同的客戶端(瀏覽器秧廉,APP伞广,客戶端等)。而配置文件和過程都會由些許調(diào)整疼电,中間最重要的角色便是AspNetCoreModule赔癌,它是其中一個的IIS模塊,請求進入到IIS之后便立即由它轉(zhuǎn)發(fā)澜沟,并迅速重定向到ASP.NET Core項目中,所以這時候我們無需設(shè)置應(yīng)用程序池來托管我們的代碼峡谊,它只負責轉(zhuǎn)發(fā)請求而已)
6茫虽、如果需要讀寫根目錄權(quán)限,要更改應(yīng)用池 ApplicationPoolIdentity
7既们、如果沒有出現(xiàn)正常的頁面濒析,你可以打開錯誤日志
在發(fā)布的時候,會有一個web.config出現(xiàn)啥纸,通過修改web.config 啟用錯誤日志查看詳細錯誤信息
將stdoutLogEnabled的修改為 true号杏,并在應(yīng)用程序根目錄添加 logs 文件夾
一定要手動添加logs文件,不然會不出現(xiàn)
8斯棒、只要本地能通過盾致,常見的錯誤就是生成的文件不全導(dǎo)致的,大家可以自行看看荣暮,如果有問題庭惜,也可以大家一起解決
9、在IIS中啟動項目穗酥,或者直接輸入服務(wù)器IP地址护赊,加端口調(diào)試
注意:這里有一個小問題,因為發(fā)布以后砾跃,默認啟動頁是在開發(fā)環(huán)境中重定向到了swagger骏啰,但是在服務(wù)器部署以后,不能跳轉(zhuǎn)抽高,大家打開后會這樣判耕,404找不到,不要怕厨内,
只需要在后邊加上Swagger就行了
三祈秕、項目在Liunx Ubuntu中部署(簡單版渺贤,慢慢完善)
1、在騰訊云購買Ubuntu服務(wù)器后请毛,登陸志鞍,然后進入命令頁面
2、部署Linux系統(tǒng)中的微軟環(huán)境
繼續(xù)執(zhí)行下面的命令
Register the trusted Microsoft signature key:
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
繼續(xù)
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
根據(jù)系統(tǒng)版本方仿,執(zhí)行下面的命令
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
好了固棚,環(huán)境部署完畢,下面我們安裝 SDK
3仙蚜、部署.Ne Core 環(huán)境
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1.4
安裝成功后此洲,輸入命令 dotnet
證明安裝成功啦
4、安裝代碼上傳工具委粉,F(xiàn)illzila或者winSCP都可以呜师,(我用的是winSCP)
軟件下好打開后界面是這樣的,我們需要填的就是主機名(你服務(wù)器的公網(wǎng)IP)贾节、用戶名(服務(wù)器的用戶名)汁汗、密碼(你買服務(wù)器時設(shè)置的密碼)博脑,那個文件協(xié)議就是SFTP谷浅,不用改變
5奖亚、登陸進去默認是 /Home/ubuntu 文件夾悬襟,我們都在這里操作
6梢什、下面我們在服務(wù)器新建一個控制臺項目測試一下
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">dotnet new console -o myApp
</pre>
然后就在winSCP發(fā)現(xiàn)多了一個項目
** 7速警、然后運行我們剛剛創(chuàng)建的項目**
cd myApp
dotnet run
代碼一起正常专筷!
8死相、把我們的項目發(fā)布上去
未完待續(xù)中...
四忿墅、結(jié)語
今天暫時就先寫到這里扁藕,我們學(xué)到了如何用AutoMapper來實現(xiàn)DTO數(shù)據(jù)對象映射,也學(xué)會了在windows下的IIS中發(fā)布項目疚脐,最后就是Linux系統(tǒng)中纹磺,搭建環(huán)境和運行.net core 。但是還沒有完成亮曹,我會慢慢補充上來橄杨,除了繼續(xù)發(fā)布以外,還包括如何桌面話Linux系統(tǒng)照卦,Nginx代理等等式矫,大家拭目以待吧
五、CODE
https://github.com/anjoy8/Blog.Core
https://gitee.com/laozhangIsPhi/Blog.Core
QQ群:
867095512 (blod.core)