EFCore Database-first深入研究
使用Scaffold-DbContext從資料庫生成實體
說明文檔:
關于 Scaffold-DbContext 微軟有官方說明文檔
https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/cli/powershell
實踐:
不妨自己找一個現有的資料庫試一試:
LocalDB
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables "Blog","Post" -ContextDir Context -Context BlogContext
MySql
Scaffold-DbContext "server=127.0.0.1;Port=3306;database=zejedb;uid=root;pwd=123456;Convert Zero Datetime=True;Allow Zero Datetime=True;Charset=utf8;Default Command Timeout =30000;" Pomelo.EntityFrameworkCore.Mysql -OutputDir Models -DataAnnotations
路漫漫
但是通過上述步驟生成的實體,跟自己想要的還是有那麼一丢丢差距。
- 比如我想通過資料庫表注釋、字段注釋,給實體、屬性加上注釋貌似就沒辦法實作。
- 比如我想統一繼承基類,将建立時間、修改時間、建立人、修改人、Id放到基類,貌似也沒辦法實作。
作為一個被抛棄的10年neter,對于自己未曾研究過源碼耿耿于懷(其實n年前我研究過jquery的部分源碼,那不算)。是以我毅然下載下傳了
EFCore
的源碼。
源碼位址:https://github.com/dotnet/efcore.git
研究EFCore的源碼

以Scaffold-DbContext作為突破口:
- 後來又看到這麼幾個類:
-
DatabaseModelFactory
-
SqlServerDatabaseModelFactory
-
DatabaseModel
-
- 還下載下傳了Pomelo.EntityFrameworkCore.MySql的源碼
- 等等
皇天不負有心人:用了一個周末的時間,關于如何添加注釋,我找到了最關鍵地方
CSharpEntityTypeGenerator
類
隻要重寫(Override)此類的
GenerateClass
,
GenerateProperties
兩個虛拟方法便可
吐槽&繼續(痛苦并快樂着)
微軟的攻城獅細節怎麼就不處理好呢?
public class CSharpEntityTypeGenerator : ICSharpEntityTypeGenerator
{
private readonly ICSharpHelper _code;
private IndentedStringBuilder _sb = null!;
private bool _useDataAnnotations;
關鍵方法:
public virtual string WriteCode(IEntityType entityType, string @namespace, bool useDataAnnotations)
{
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(@namespace, nameof(@namespace));
_sb = new IndentedStringBuilder();
_useDataAnnotations = useDataAnnotations;
_sb.AppendLine("using System;");
_sb.AppendLine("using System.Collections.Generic;");
if (_useDataAnnotations)
{
_sb.AppendLine("using System.ComponentModel.DataAnnotations;");
_sb.AppendLine("using System.ComponentModel.DataAnnotations.Schema;");
}
foreach (var ns in entityType.GetProperties()
.SelectMany(p => p.ClrType.GetNamespaces())
.Where(ns => ns != "System" && ns != "System.Collections.Generic")
.Distinct()
.OrderBy(x => x, new NamespaceComparer()))
{
_sb.AppendLine($"using {ns};");
}
_sb.AppendLine();
_sb.AppendLine($"namespace {@namespace}");
_sb.AppendLine("{");
using (_sb.Indent())
{
GenerateClass(entityType);
}
_sb.AppendLine("}");
return _sb.ToString();
}
生成實體全然靠
WriteCode
方法,為什麼
sb
不公開?(後來才知道,他們合并代碼有這麼一個校驗:Public_inheritable_apis_should_be_virtual)
無奈之下,我把整個類都拷貝出來,稍微修改,添加上關鍵代碼
- 類注釋:
var comment = entityType.FindAnnotation("Relational:Comment");
if (comment != null && comment.Value != null)
{
_sb.AppendLine("///<summary>");
_sb.AppendLine("///" + comment.Value.ToString());
_sb.AppendLine("///</summary>");
}
- 實體注釋:
var comment = property.FindAnnotation("Relational:Comment");
if (comment != null && comment.Value != null)
{
_sb.AppendLine("///<summary>");
_sb.AppendLine("///" + comment.Value.ToString());
_sb.AppendLine("///</summary>");
}
- 測試結果:
EFCore Database-first深入研究
總結
生成實體的工具那麼多,何必糾纏
Scaffold-DbContext
不放手?
其實:
1、個人有代碼潔癖。
2、有代碼注釋強迫症。
3、給自己找個強迫自己看源碼的理由。
其實實作自動生成代碼并帶上注釋的,個人已經有實作的辦法,但是不完美,有些地方還是需要手動修改。糾纏
Scaffold-DbContext
是另外一種嘗試罷了。
放棄 Scaffold-DbContext
,使用T4模闆
Scaffold-DbContext
為何放棄
難以自定義
- 無法帶注釋(上述幾行代碼已在github上推送了),不過Scaffold-DbContext要支援注釋估計是猴年馬月的事情了。
- 不知道怎麼繼承基類等
PackageReferences在T4模闆下的水土不服
使用CopyLocalLockFileAssemblies,将dll複制到bin目錄下
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>
T4模闆中引用DLL
<#@ assembly name="$(TargetDir)\Microsoft.EntityFrameworkCore.dll" #>
<#@ assembly name="$(TargetDir)\Microsoft.EntityFrameworkCore.Design.dll" #>
其實本人之前是使用自己寫的
Zeje.T4_.dll
來生成的,現在計劃基于
Microsoft.EntityFrameworkCore.dll
及其擴充類庫進行研究,下個周末繼續研究。
作者:哲智(Zeje)
出處:http://www.cnblogs.com/zeje/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。