有朋友說老週近來博客更新較慢,確實有些慢,由於有些 bug 要研究,另外就是老周把部份內容轉到直播上面,因此寫博客的內容減小了一點。前端
老周以爲,視頻直播可能會好一些,雖然個人水平通常,不過直播時,老周能夠現場演示,可能會比看博客效果要好(由於現場演示,有時候會有失誤,沒辦法,水平有限)。還有一個,就是.NET 的資料其實不少,畢竟也發展了十幾年了,有些東西若是別人都寫過了,那我也很差意思重複了。.NET Core 儘管是跨平臺版本,但核心依然是.net 基礎,咱們不須要全新去學習,只要掌握一些新的變化就能夠了。目前比較期待 .NET Core 3 的正式發佈,等正式上線了,老周再挑一些有意義的內容寫一下。數據庫
此外,老周也可能會寫一寫其餘方面的博客,好比 Python、GO、Ruby、Typescript 等。老周並非只會玩.NET ,只不過老周是主攻 .NET,在接觸 .NET 以前,老周就學過不少東西,好比古老的 QBasic、Pascal ,老周在上初中時就學過。後來向 VB、C、C++ 進攻,順便把 Ruby、Python、PB 也調戲一下,後來有一段時間,Delphi 和 E 語言也挺流行的,因此順便也玩了兩把。編程
再後來,學過 Java 和 PHP,拋 Java 而投 .NET 是由於 Java 太複雜,效率不高,沒有深度把玩的興趣。如今所謂的 Python 很熱門純屬是商業炒做,Python 又不是什麼新玩意兒,很古老了,固然相對於 C 來講,是新了一點,究其特色,就是一種腳本語言(雖然有人死要說它不是腳本語言)。如今網上更有些無知小輩,覺得本身會寫幾行 Python 代碼就處處去蹭熱點,告訴你,老周當年學各類編程語言時,說不定你還沒出生呢。因此,若是你真心喜歡 Python 的話,你用心去學就是了(其實老周也喜歡用 Python 來作圖表),沒必要理會商業炒做。api
記得去年 C 語言也被商業炒做了幾個月,再往前幾年,Javascript 和 Web 前端也被拼命炒做,說得好像 js 是萬能的似的,嚇得老周都不敢寫前端了。最近幾年,IT 界開始懷舊了,各類遠古生物都被挖出來了,多是如今計算機行業已經沒什麼能夠創新的緣由吧。如今說得較多的是人工智障,這個能夠用,也能夠不用,反正不痛不癢,算不上生產力革命(至少其震動效果比不上當年 Office 問世時對企業生產的影響大)。不過,人工智障在某些輔助領域仍是有用的,好比如今有些小區的智能門,應用效果還能夠。可是,漏洞也是百出的,總之,人類能夠用它來進行輔助,但不能過於依賴它。它不能解決全部問題。數組
許多科幻小說都會說人類會被機器人消滅。機器人也是人創造出來的,機器不可能比人強,也不可能滅掉人類(除非機器人比奧特曼裏面的超獸還牛逼)。若是人類真的智力在衰減,那麼根源仍是在人類本身。說白了就是,只有多是人類本身滅掉本身。你也不用以爲太恐怖,其實只要你不要太依賴機器就好,不要失去你的本能和思考方式就行。編程語言
就像咱們碼農,老周也同樣,每天跟計算機打交道,但老週一直堅持:用電腦,但不依賴電腦,多作些機器不能作的事。再加一句:科學只能解決數學和工程問題,而人的問題,須要哲學和美學來解決。ide
=============================================================================函數
好了,以上的都是 F 話,下面我們聊正題。今天我們耍一下 EF Core 中的影子屬性。這個詞翻譯版本 TMD 多,有翻譯爲「卷影屬性」的,如今的文檔又改成「陰影屬性」,這很差聽,太有心理陰影了,故而,老周以爲,叫「影子屬性」好一些。學習
無論叫什麼,你只要知道它是個啥就行。老周喜歡一句話總結,因此,來一句話:測試
模型類中沒有定義的,但數據表中存在的屬性——即模型類與數據表中沒有對應關係的屬性。
老周就用一個簡單的示例來講明一下吧。這個示例也是老周在視頻直播時用的。
假設,有個模型類,叫 Student。
public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
但我想要一個屬性,用來記錄數據記錄被寫進數據庫的時間,即還有一個屬性,叫 InsertTime,不過,這個屬性在 Student 類中是沒有定義的,但在數據表中是有這一字段的。
所以,在從 DbContext 類派生時,須要重寫 OnModelCreating 方法,經過 Model Builder 來定義這個「隱藏」的屬性。
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Property<DateTime>("InsertTime"); }
這個 InsertTime 屬性就成了影子屬性了。
下面是 DbContext 派生類的完整代碼,我放出來是方便你去抄襲的,放心吧,無版權稅的,儘管抄。
public class MyContext : DbContext { public DbSet<Student> Students { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"server=(localdb)\MSSQLLocalDB;database=TestDb"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Property<DateTime>("InsertTime"); } }
如今,到 Startup 類中,註冊一下服務,讓自定義的數據庫上下文能夠進行依賴注入。
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDbContext<MyContext>(); }
剛剛在 MyContext 類中已經配置鏈接字符串了,因此註冊服務時就不用指定鏈接字符串了。
按老周前面所寫的博文,接下來是建立數據遷移,不過,這一次老周使用的是直接在運行的時候建立數據庫,方法也是很簡單的,找到 Main 入口點,把裏面的代碼改一下(項目模板默認生成的代碼不進行大改)。
public static void Main(string[] args) { var host = CreateWebHostBuilder(args).Build(); using(IServiceScope scope= host.Services.CreateScope()) { MyContext c = scope.ServiceProvider.GetService<MyContext>(); c.Database.EnsureCreated(); } host.Run(); }
也就是在 host 運行以前,建立一個「做用域」級別的服務實例,建立數據庫,這個實例是臨時使用,不遵循服務容器的生命週期規則。EnsureCreated 方法會檢測數據庫是否存在,若是不存在,就建立,而後返回 true;若是數據庫已經存在,不作任何處理並返回 false。
若是你不打算寫入一些初始數據,能夠不在意方法的返回值,若是要寫入初始數據,能夠 if 一下,若是方法返回 true,就寫一下數據進去(true 表示新數據庫)。
接下來,我們用一個 API 控制器來測試一下。
[Route("api/[action]")] public class TestController : Controller { readonly MyContext context; public TestController(MyContext c) => context = c; [HttpPost] public ActionResult AddNew([FromBody]Student stu) { …… } [HttpGet] public JArray GetList() { …… } }
數據庫上下文的實例由於已經註冊到服務容器中,因此經過構造函數能夠獲得其實例引用。這個控制器有兩個 action,AddNew 方法用來提交數據,以 POST 方式訪問。
[HttpPost] public ActionResult AddNew([FromBody]Student stu) { // 添加實體 context.Students.Add(stu); // 設置影子屬性 context.Entry(stu).Property<DateTime>("InsertTime").CurrentValue = DateTime.Now; context.SaveChanges(); return Ok("操做成功"); }
記得,參數要加上 FromBody 特性,由於它要從 HTTP 消息正文中提取,上次我直播時就是忘了寫這個,因此提交不到數據。
這裏要注意影子屬性的賦值方法,由於它沒有在 Student 類中公開,你不能經過訪問成員來設置它,只能先經過 CurrentValue 屬性來設置。
而後還有一個 GetList 方法,以 GET 方式來訪問。有來返回全部 Student 數據。此處我用 JArray 以 JSON 數組格式返回。
[HttpGet] public JArray GetList() { var q = from s in context.Students select new { s.Id, s.Name, s.Age, // 讀取影子屬性值 InsertTime = EF.Property<DateTime>(s, "InsertTime") }; JArray arr = new JArray(); foreach (var s in q) { JObject obj = new JObject(); obj.Add("id", new JValue(s.Id)); obj.Add("name", new JValue(s.Name)); obj.Add("age", new JValue(s.Age)); obj.Add("add_time", new JValue(s.InsertTime)); arr.Add(obj); } return arr; }
讀取影子屬性的方法是用 EF.Property 靜態方法,第一個參數是實體模型類的實例,第二個參數是影子屬性的名字。這個方法只能在 LINQ 樹中使用,若是不在 LINQ 中用會發生異常。這裏還有一個問題,就是在 select 子句中用 new 返回的匿名類型沒法建立 JObject 對象,因此只好手動去構建。
如今能夠測一下了,首先提交一下數據。
{ "name":"李三跳", "age":52 }
接着獲取一下數據列表。
[ { "id": 1, "name": "什麼鬼", "age": 29, "add_time": "2018-11-26T11:15:38.2012809" }, { "id": 2, "name": "陳大扣", "age": 83, "add_time": "2018-11-26T11:21:07.5374308" }, { "id": 3, "name": "李三跳", "age": 52, "add_time": "2018-11-26T12:41:51.758849" } ]
好了,示例就完成了。
影子屬性的典型用法就像剛剛這個例子這樣,能夠用來記錄數據的添加時間或者更新時間,但這種數據,通常不須要在實體模型中公開。