【ASP.NET Core】EF Core - 「影子屬性」

有朋友說老週近來博客更新較慢,確實有些慢,由於有些 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"
    }
]

 

好了,示例就完成了。

影子屬性的典型用法就像剛剛這個例子這樣,能夠用來記錄數據的添加時間或者更新時間,但這種數據,通常不須要在實體模型中公開。

相關文章
相關標籤/搜索