Entity Framework Code First易忘點

EF入門基礎知識瞭解

1、DbContext(上下文類)
DbContext是實體類與數據庫的橋樑,主要負責與數據交互。
在這裏插入圖片描述
DbContext主要負責以下活動:

EntitySet::DbContext包含了所有映射到表的entities

Querying:將Linq-To-Entities轉譯爲Sql併發送到數據庫

Change Tracking:從數據庫獲取entities後保留並跟蹤實體數據變化

Persisting Data:根據entity狀態執行Insert、update、delete命令

Caching:DbContext的默認第一級緩存,在上下文中的生命週期中存儲entity

Manage Relationship:DbContext在DbFirst模式中使用CSDL、MSL、SSDL管理對象關係,Code first中使用fluent api 管理關係

Object Materialization:DbContext將物理錶轉成entity實例對象

Entity Framework Code First屬性映射約定
  Entity Framework Code First與數據表之間的映射方式有兩種實現:Data Annotation和Fluent API。本文中採用創建Product類爲例來說明tity Framework Code First屬性映射約定的具體方式。

  1. 表名及所有者
      在默認約定的情況下,Entity Framework Code First創建的表名是根據類名的英語複數形式,創建的表所有者爲dbo,可以通過重寫約定來指定表名及表的所有者。

1.1 Data Annotation方式
  在使用Data Annotation方式進行Entity Framework Code First與數據庫映射之前,需要先添加命名空間引用。

using System.ComponentModel.DataAnnotations.Schema;

爲類配置對應表名:

[Table("Product")]
public class Product

爲類配置對應表名並指定表的所有者:

[Table("Product", Schema = "dbo")]
public class Product
1.2 Fluent API方式

Fluent API實現配置Entity Framework Code First與數據庫映射關係主要是通過繼承DbContext並重寫其中的OnModelCreating方法來進行的。在本文中新建類文件PortalContext.cs繼承DbContext。

在繼承DbContext之前,添加命名空間引用。

using System.Data.Entity;
  重寫OnModelCreating方法,配置類對應於數據庫中的表名:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().ToTable("Product");
}

重寫OnModelCreating方法,配置類對應於數據庫中的表名,並指定表的所有者:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().ToTable("Product", "dbo");
}
 

到此處PortalContext.cs的完整代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data.Entity;

using Portal.Entities;

namespace Portal
{
    public class PortalContext : DbContext
    {
        static PortalContext()
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<PortalContext>());
        }

        public PortalContext()
            : base("name=PortalContext")
        {
        }

        public DbSet<Product> Products { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Product>().ToTable("Product", "dbo");
        }
    }
}
  1. 字段名、長度、數據類型及是否可空
      在默認約定的情況下,Entity Framework Code First創建的列名與類的屬性名相同,可以根據需要進行重新指定類屬性與列名之間的映射關係。

2.1 Data Annotation方式

[Column("ProductID")]
public int ProductID { get; set; }

[MaxLength(100)]
[Required, Column("ProductName")]
public string ProductName { get; set; }

在使用Required特性(Attribute)設置字段不允許爲空時,需要添加命名空間引用:

using System.ComponentModel.DataAnnotations;
1.2 Fluent API方式

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().Property(t => t.ProductID)
        .HasColumnName("ProductID");
    modelBuilder.Entity<Product>().Property(t => t.ProductName)
        .IsRequired()
        .HasColumnName("ProductName")
     .HasMaxLength(100);
}

在默認情況下,int類型的屬性生成的列名對應SQL SERVER列int類型;而String類型的屬性則對應SQL SERVER列的NVARCHAR類型。若類的字符串類型屬性未設置MaxLength,則生成對應的列類型爲NVARCHAR(MAX)。

爲屬性指定對應的SQL SERVER數據類型:

[Column("UnitPrice", TypeName = "MONEY")]
public decimal UnitPrice { get; set; }
modelBuilder.Entity<Product>().Property(t => t.UnitPrice)
    .HasColumnName("UnitPrice")
    .HasColumnType("MONEY");

到此步,Product.cs類文件的完整代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Portal.Entities
{
    [Table("Product", Schema = "dbo")]
    public class Product
    {
        [Column("ProductID")]
        public int ProductID { get; set; }

        [MaxLength(100)]
        [Required, Column("ProductName")]
        public string ProductName { get; set; }

        [Column("UnitPrice", TypeName = "MONEY")]
        public decimal UnitPrice { get; set; }
    }
}

屬性設置text數據類型:

[Column("Remark", TypeName = "text")]
public string Remark { get; set; }
modelBuilder.Entity<Category>().Property(t => t.Remark)
    .HasColumnName("Remark")
    .HasColumnType("text");
  1. 主鍵
      Entity Framework Code First的默認主鍵約束:屬性名爲[ID]或[類名 + ID]。如在Product類中,Entity Framework Code First會根據默認約定將類中名稱爲ID或ProductID的屬性設置爲主鍵。Entity Framework Code First主鍵的默認約定也一樣可以進行重寫,重新根據需要進行設置。

3.1 Data Annotation方式

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Key]
[Column("ProductID")]
public int ProductID { get; set; }

3.2 Fluent API方式

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().HasKey(t => t.ProductID);
}

若一個表有多個主鍵時:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().HasKey(t => new { t.KeyID, t.CandidateID });
}

4、數據庫自動生成字段值
  Entity Framework Code First對於int類型的主鍵,會自動的設置其爲自動增長列。但有時我們確實不需是自動增長的,可以通過以下方式進行取消自動增長。

4.1 Data Annotation方式

[Key]
[Column("ProductID")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductID { get; set; }
[Key]
[Column("CategoryID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryID { get; set; }

4.2 Fluent API方式
p

rotected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().HasKey(t => t.ProductID);
    modelBuilder.Entity<Product>().Property(t => t.ProductID)
        .HasColumnName("ProductID")
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Category>().ToTable("Category", "dbo");
    modelBuilder.Entity<Category>().HasKey(t => t.CategoryID);
    modelBuilder.Entity<Category>().Property(t => t.CategoryID)
        .HasColumnName("CategoryID")
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
  1. 數字類型長度及精度
      在Product類中,UnitPrice表示單價,對於價格類的字段,我們通常會希望其保留2爲小數。這時可以使用Fluent API進行設置,且Data Annotation不支持該設置。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    modelBuilder.Entity().Property(t => t.UnitPrice)
    .HasColumnName(“UnitPrice”)
    .HasPrecision(18, 2);
    }

6、非數據庫字段屬性
  在類中,如果有一些屬性不需要映射到對應生成的數據表中,可以通過以下方式設置。

6.1 Data Annotation方式

[NotMapped]
public string Remark { get; set; }
6.2 Fluent API方式
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().Ignore(t => t.Remark);
}
  1. Fluent API配置Configuration映射類
      在使用Fluent API進行Entity Framework Code First數據庫映射時,除了以上的在重寫OnModelCreating方法中直接對Entity進行配置之外,也可以對Configurations進行配置。這時可以先寫一個單獨的類,將數據表的全部映射要求都寫在構造函數中。

ProductMap.cs類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

using Portal.Entities;

namespace Portal.Mapping
{
    public class ProductMap : EntityTypeConfiguration<Product>
    {
        public ProductMap()
        {
            // Primary Key
            this.HasKey(t => t.ProductID);

            // Properties
            this.Property(t => t.ProductID)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(t => t.ProductName)
                .IsRequired()
                .HasMaxLength(100);

            // Table & Column Mappings
            this.ToTable("Product");
            this.Property(t => t.ProductID).HasColumnName("ProductID");
            this.Property(t => t.ProductName).HasColumnName("ProductName");
            this.Property(t => t.UnitPrice)
                .HasColumnName("UnitPrice")
                .HasPrecision(18, 2);
        }
    }
}

有了上面的映射類之後,在重寫OnModelCreating方法中則可以直接調用映射類,從而減少了OnModelCreating方法的複雜度,同時也增強了代碼維護的可讀性。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new ProductMap());
}

Code First Migrations更新數據庫具體步驟
以下內容轉自(https://blog.csdn.net/cityhunter172/article/details/8062420)
需要置好相關參數 以及DbContext
1、配置數據庫連接字串

在項目中,找到 app.config(沒有則在項目根目錄手動新增,這裏的設置只對本項目有效,不會影響到 Web 項目中的設置)。配置 節點,新增 ,Mvc4DAL 用的是名稱 MyDBConnectString 的連接字串
publicclassSchoolContext : DbContext

{

publicSchoolContext() : base(“MyDBConnectString”)

若沒有設置好連接字串,或是字串設置有誤,將出現如下提示:
Anerror occurred while getting provider information from the database. This canbe caused by Entity Framework using an incorrect connection string. Check theinner exceptions for details and ensure that the connection string is correct.

2、運行命令Enable-Migrations

出現如下提示時,你需要執行 Enable-Migrations:
Nomigrations configuration type was found in the assembly ‘Mvc4DAL’. (In VisualStudio you can use the Enable-Migrations command from Package Manager Consoleto add a migrations configuration).

打開“程序包管理器控制檯”,運行命令 Enable-Migrations ,Mvc4DAL項目中將出現 Migrations文件夾與相應的文件 Configuration.cs

執行 Enable-Migrations 時可能會因爲錯誤而打斷,此時需要再次運行加參數的命令Enable-Migrations -Force:
Migrationshave already been enabled in project ‘Mvc4DAL’. To overwrite the existingmigrations configuration, use the -Force parameter.
注意“Enable-Migrations”中間沒有空格,“-Force”前面必須要有空格。

3、設置 AutomaticMigrationsEnabled爲 true

打開Migrations文件夾中的 Configuration.cs,AutomaticMigrationsEnabled默認爲 false 改爲 true就哦了,否則將出現提示:
Unable to update database to match the current model because there arepending changes and automatic migration is disabled. Either write the pendingmodel changes to a code-based migration or enable automatic migration. SetDbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enableautomatic migration. You can use the Add-Migration command to write the pendingmodel changes to a code-based migration.
4、最後執行 Update-Database

上述步驟都設置好了,打開“程序包管理器控制檯”,運行命令 Update-Database,沒有出錯就大功成。這裏要注意的是,數據庫中有個名稱爲dbo.__MigrationHistory 的Table很重要,記錄的是從創建數據庫開始的全部更新的記錄,所以在你沒有絕對把握的情況下千萬別動它。
5、Add-Migration
在Package Manager Console中執行Add-Migration搭建掛起的Model變化遷移腳本。
以下爲EF遷移命令
https://www.cnblogs.com/nianming/p/3439260.html

EF Code First 導航屬性 與外鍵
轉自 https://www.cnblogs.com/liangxiaofeng/p/5809451.html
Code First對一對多關係也有着很好的支持。很多情況下我們都不需要特意的去配置,Code First就能通過一些引用屬性、導航屬性等檢測到模型之間的關係,自動爲我們生成外鍵。
觀察下面的類:

public class Destination
    {
        public int DestinationId { get; set; }
        public string Name { get; set; }
        public string Country { get; set; }
        public string Description { get; set; }
        public byte[] Photo { get; set; }
        public List<Lodging> Lodgings { get; set; }
    }

    public class Lodging
    {
        public int LodgingId { get; set; }
        public string Name { get; set; }
        public string Owner { get; set; }
        public bool IsResort { get; set; }
        public decimal MilesFromNearestAirport { get; set; }
        public Destination Destination { get; set; }
    }

Code First觀察到Lodging類中有一個對Destination的引用屬性,同時Destination中又有一個集合導航屬性Lodgings,因此推測出Destination與Lodging的關係是一對多關係,所以在生成的數據庫中爲自動爲Lodging表生成外鍵:
在這裏插入圖片描述
其實,只要在一個類中存在引用屬性,即:

public class Destination
    {
        public int DestinationId { get; set; }
        public string Name { get; set; }
        public string Country { get; set; }
        public string Description { get; set; }
        public byte[] Photo { get; set; }
    }

    public class Lodging
    {
        public int LodgingId { get; set; }
        public string Name { get; set; }
        public string Owner { get; set; }
        public bool IsResort { get; set; }
        public decimal MilesFromNearestAirport { get; set; }
        public Destination Destination { get; set; }
    }

或一另一個類中存在導航屬性:
public class Destination
{
public int DestinationId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Description { get; set; }
public byte[] Photo { get; set; }
public List Lodgings { get; set; }
}

public class Lodging
{
    public int LodgingId { get; set; }
    public string Name { get; set; }
    public string Owner { get; set; }
    public bool IsResort { get; set; }
    public decimal MilesFromNearestAirport { get; set; }
}

Code First都能檢測到它們之間一對多的關係,自動生成外鍵。
指定外鍵

當然我們也可以自己在類中增加一個外鍵。默認情況下,如果你的外鍵命名是規範的話,Code First會將的該屬性設置爲外鍵,不再自動創建一個外鍵,如:

 public class Destination
    {
        public int DestinationId { get; set; }
        public string Name { get; set; }
        public string Country { get; set; }
        public string Description { get; set; }
        public byte[] Photo { get; set; }
        public List<Lodging> Lodgings { get; set; }
    }

    public class Lodging
    {
        public int LodgingId { get; set; }
        public string Name { get; set; }
        public string Owner { get; set; }
        public bool IsResort { get; set; }
        public decimal MilesFromNearestAirport { get; set; }
        //外鍵 
        public int TargetDestinationId { get; set; }
        public Destination Target { get; set; }
    } 

規範命名是指符合:命名爲“[目標類型的鍵名],[目標類型名稱]+[目標類型鍵名稱]”,或“[導航屬性名稱]+[目標類型鍵名稱]”的形式,在這裏目標類型就是Destination,相對應的命名就是:DestinationId,DestinationDestinationId,TargetDestinationId
對於命名不規範的列,Code First會怎做呢?

比如我們將外鍵改爲:

public int TarDestinationId { get; set; }

再重新生成數據庫:

可以看到Code First沒有識別到TarDestinationId是一個外鍵,於是自己創建了一個外鍵:Target_DestinationId。這時我們要告訴Code First該屬性是一個外鍵。

使用Data Annotations指定外鍵:

[ForeignKey("Target")]
public int TarDestinationId { get; set; }
public Destination Target { get; set; }

public int TarDestinationId { get; set; }
[ForeignKey("TarDestinationId")]
public Destination Target { get; set; }

注意ForeignKey位置的不同,其後帶的參數也不同。這樣,生成的數據庫就是我們所期望的了。Code First沒有再生成別的外鍵

**

EF裏實體關係配置的方法

**
EF裏的實體關係配置分爲Has和With系列的方法:Optional 可選的、Required 必須的、Many 多個
Has方法:
1、HasOptional:前者包含後者一個實例或者爲null
2、HasRequired:前者(A)包含後者(B)一個不爲null的實例
3、HasMany:前者包含後者實例的集合**
With方法:
1、WithOptional:後者(B)可以包含前者(A)一個實例或者null
2、WithRequired:後者包含前者一個不爲null的實例
WithMany:後者包含前者實例的集合

A.HasRequired(a => a.B).WithOptional(b => b.A);

上面一句配置意思就是A類包含B類一個不爲null的實例,B類包含A類一個實例,也可以不包含。最標準的一對一配置。ok,現在試着寫下上面Person類和PersonPhoto類的一對一的關係如何配置:

this.HasRequired(p => p.PhotoOf).WithOptional(p => p.Photo);

再跑下程序,數據庫就生成了,是一對一的關係。Person表可以沒有對應的PersonPhoto表數據,但是PersonPhoto表每一條數據都必須對應一條Person表數據。意思就是人可以沒有照片,但是有的照片必須屬於某個人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章