EntityFramework筆記

參照文檔: http://www.cnblogs.com/farb/p/ABPAdvancedTheoryContent.htmlhtml

案例:http://pan.baidu.com/s/1c1Qgg28web

 

1、領域建模和管理實體關係sql

2、 使用LINQ to Entities操做實體數據庫

3、預加載數組

4、CURD安全

5、EF使用視圖服務器

6、EF使用存儲過程微信

7、異步API併發

8、管理併發app

9、事務

10、數據庫遷移

11、應用遷移

12、EF的其餘功能

 

1、領域建模和管理實體關係

 1,流利地配置領域類到數據庫模式的映射

namespace FirstCodeFirstApp
{
    public class Context:DbContext
    {
        public Context()
            : base("name=FirstCodeFirstApp")
        {
        }

        public DbSet<Donator> Donators { get; set; }
        public DbSet<PayWay> PayWays { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Donator>().ToTable("Donators").HasKey(m => m.DonatorId);//映射到表Donators,DonatorId看成主鍵對待
            modelBuilder.Entity<Donator>().Property(m => m.DonatorId).HasColumnName("Id");//映射到數據表中的主鍵名爲Id而不是DonatorId
            modelBuilder.Entity<Donator>().Property(m => m.Name)
                .IsRequired()//設置Name是必須的,即不爲null,默認是可爲null的
                .IsUnicode()//設置Name列爲Unicode字符,實際上默認就是unicode,因此該方法可不寫
                .HasMaxLength(10);//最大長度爲10

            base.OnModelCreating(modelBuilder);
        }
    }
}

1.1,每一個實體類單首創建一個配置類。而後再在OnModelCreating方法中調用這些配置夥伴類

public class DonatorMap:EntityTypeConfiguration<Donator>
{
    public DonatorMap()
    {
        ToTable("DonatorFromConfig");//爲了區分以前的結果
        Property(m => m.Name)
            .IsRequired()//將Name設置爲必須的
            .HasColumnName("DonatorName");//爲了區別以前的結果,將Name映射到數據表的DonatorName
    }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
       modelBuilder.Configurations.Add(new DonatorMap());
       base.OnModelCreating(modelBuilder);
}

 2,一對多關係

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Donator>().HasMany(r => r.PayWays)//HasMany方法告訴EF在Donator和Payway類之間有一個一對多的關係
                .WithRequired()//WithRequired方法代表連接在PayWays屬性上的Donator是必須的,換言之,Payway對象不是獨立的對象,必需要連接到一個Donator
                .HasForeignKey(r => r.DonatorId);//HasForeignKey方法會識別哪個屬性會做爲連接
            base.OnModelCreating(modelBuilder);
        }

2.1,指定約束的刪除規則

public class DonatorTypeMap:EntityTypeConfiguration<DonatorType>
{
    public DonatorTypeMap()
    {
        HasMany(dt=>dt.Donators)
            .WithOptional(d=>d.DonatorType)
            .HasForeignKey(d=>d.DonatorTypeId)
            .WillCascadeOnDelete(false);//指定約束的刪除規則
    }
}

2.2調用WillCascadeOnDelete的另外一種選擇是,從 model builder中移除全局的約定,在數據庫上下文的OnModelCreating方法中關閉整個數據庫模型的級聯刪除規則

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{

    modelBuilder.Configurations.Add(new DonatorMap());
    modelBuilder.Configurations.Add(new DonatorTypeMap());
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
    base.OnModelCreating(modelBuilder);
}

 2.3建立一對多關係的代碼

#region 6.1 一對多關係 例子2

var donatorType = new DonatorType
{
    Name = "博客園園友",
    Donators = new List<Donator>
    {
        new Donator
        {
            Amount =6,Name = "鍵盤裏的鼠標",DonateDate =DateTime.Parse("2016-4-13"),
            PayWays = new List<PayWay>{new PayWay{Name = "支付寶"},new PayWay{Name = "微信"}}
        }     
    }
};
var donatorType2 = new DonatorType
{
    Name = "非博客園園友",
    Donators = new List<Donator>
    {

         new Donator
        {
            Amount =10,Name = "待贊助",DonateDate =DateTime.Parse("2016-4-27"),
            PayWays = new List<PayWay>{new PayWay{Name = "支付寶"},new PayWay{Name = "微信"}}
        }
        
    }
};
context.DonatorTypes.Add(donatorType);
context.DonatorTypes.Add(donatorType2);
context.SaveChanges();

 

3,一對一關係

public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public virtual Student Student { get; set; }
}


public class Student
{
    public int PersonId { get; set; }
    public virtual Person Person { get; set; }
    public string CollegeName { get; set; }
    public DateTime EnrollmentDate { get; set; }
}
public class StudentMap:EntityTypeConfiguration<Student>
{
    public StudentMap()
    {
        HasRequired(s=>s.Person)
            .WithOptional(p=>p.Student);//一或零對一
        HasKey(s => s.PersonId);
        Property(s => s.CollegeName)
            .HasMaxLength(50)
            .IsRequired();
    }
}
var student = new Student
{
    CollegeName = "XX大學",
    EnrollmentDate = DateTime.Parse("2011-11-11"),
    Person = new Person
    {
        Name = "Farb",
    }
};

context.Students.Add(student);
context.SaveChanges();

4,多對多

public class Company
{
     public Company()
     {
        Persons = new HashSet<Person>();
     }
    public int CompanyId { get; set; }
    public string CompanyName { get; set; }
    public virtual ICollection <Person> Persons { get; set; }
}

public class Person
{
    public Person()
    {
        Companies=new HashSet<Company>();
    }
    public int PersonId { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public virtual Student Student { get; set; }
    public virtual ICollection<Company> Companies { get; set; }
}

public class PersonMap:EntityTypeConfiguration<Person>
{
    public PersonMap()
    {
        HasMany(p => p.Companies)
            .WithMany(c => c.Persons)
            .Map(m =>
            {
                m.MapLeftKey("PersonId");
                m.MapRightKey("CompanyId");
            });
    }
}
#region 8 多對多關係

var person = new Person
{
    Name = "比爾蓋茨",
};
var person2 = new Person
{
    Name = "喬布斯",
};
context.People.Add(person);
context.People.Add(person2);
var company = new Company
{
    CompanyName = "微軟"
};
company.Persons.Add(person);
context.Companies.Add(company);
context.SaveChanges();

#endregion

 

 

 若是咱們鏈接表須要保存更多的數據怎麼辦?好比當每一個人開始爲公司幹活時,咱們想爲他們添加僱傭日期。這樣的話,實際上咱們須要建立一個類來模型化該鏈接表,咱們暫且稱爲PersonCompany吧。它仍然具備兩個的主鍵屬性,PersonId和CompanyId,它還有Person和Company的屬性以及僱傭日期的屬性。此外,Person和Company類分別都有PersonCompanies的集合屬性而不是單獨的Person和Company集合屬性。

 

 

 5,Table per Type(TPT)繼承

 派生類加上數據註解,代表他們是獨立的表

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
}

[Table("Employees")]
public class Employee : Person
{
    public decimal Salary { get; set; }
}

[Table("Vendors")]
 public class Vendor : Person
 {
     public decimal HourlyRate { get; set; }
 }
 public class Context:DbContext
 {
     public virtual DbSet<Person> People { get; set; }//上面的上下文中,咱們只添加了實體Person的DbSet。由於其它的兩個領域模型都是從這個模型派生的,因此咱們也就至關於將其它兩個類添加到了DbSet集合中了,這樣EF會使用多 態性來使用實際的領域模型
 }
#region 1.0  TPT繼承

var employee = new Employee
{
    Name = "farb",
    Email = "farbguo@qq.com",
    PhoneNumber = "12345678",
    Salary = 1234m
};

var vendor = new Vendor
{
    Name = "tkb至簡",
    Email = "farbguo@outlook.com",
    PhoneNumber = "78956131",
    HourlyRate = 4567m
};

context.People.Add(employee);
context.People.Add(vendor);
context.SaveChanges();
#endregion

 

6,Table per Class Hierarchy(TPH)繼承

 

 未使用數據註解指點派生類的代表

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
}


 public class Employee : Person
 {
     public decimal Salary { get; set; }
 }


public class Vendor : Person
{
    public decimal HourlyRate { get; set; }
}
public class Context:DbContext
{
    public Context():base("ThreeInheritance")
    {
        
    }

    public virtual DbSet<Person> Person { get; set; }
}
#region 2.0 TPH 繼承
  var employee = new Employee
  {
      Name = "farb",
      Email = "farbguo@qq.com",
      PhoneNumber = "12345678",
      Salary = 1234m
  };
 
  var vendor = new Vendor
  {
      Name = "tkb至簡",
      Email = "farbguo@outlook.com",
      PhoneNumber = "78956131",
      HourlyRate = 4567m
  };
 
  context.Person.Add(employee);
  context.Person.Add(vendor);
  context.SaveChanges();
 #endregion

 

7,Table per Concrete Class(TPC)繼承

若是咱們想使用TPC繼承,那麼要麼使用基於GUID的Id,要麼從應用程序中傳入Id,或者使用可以維護對多張表自動生成的列的惟一性的某些數據庫機制

public abstract class Person
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
}

public class Vendor : Person
{
    public decimal HourlyRate { get; set; }
}

public class Employee : Person
{
    public decimal Salary { get; set; }
}
public virtual DbSet<Person> People { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>().Map(m =>
    {
        m.MapInheritedProperties();//方法將繼承的屬性映射到表中,而後咱們根據不一樣的對象類型映射到不一樣的表中
        m.ToTable("Employees");
    });

    modelBuilder.Entity<Vendor>().Map(m =>
    {
        m.MapInheritedProperties();
        m.ToTable("Vendors");
    });
    base.OnModelCreating(modelBuilder);
}MapInheritedProperties

2、 使用LINQ to Entities操做實體

 1,實現多表鏈接join

var join1 = from province in db.Provinces
            join donator in db.Donators on province.Id equals donator.Province.Id
            into donatorList//注意,這裏的donatorList是屬於某個省份的全部打賞者,不少人會誤解爲這是兩張表join以後的結果集
            select new
            {
                ProvinceName = province.ProvinceName,
                DonatorList = donatorList
            };

var join2 = db.Provinces.GroupJoin(db.Donators,//Provinces集合要鏈接的Donators實體集合
    province => province.Id,//左表要鏈接的鍵
    donator => donator.Province.Id,//右表要鏈接的鍵
    (province, donatorGroup) => new//返回的結果集
    {
        ProvinceName = province.ProvinceName,
        DonatorList = donatorGroup
    }
    );

3、預加載

 預加載是這樣一種過程,當咱們要加載查詢中的主要實體時,同時也加載與之相關的實體。要實現預加載,咱們要使用Include方法。下面咱們看一下如何在加載Donator數據的時候,同時也預先加載全部的Provinces數據:

//預加載,如下兩種方式均可以
var donators2 = db.Donators.Include(d => d.Province).ToList();
var donators3 = db.Donators.Include("Provinces").ToList();

這樣,當咱們從數據庫中取到Donators集合時,也取到了Provinces集合。

 

4、CURD

狀態 描述
Added 添加了一個新的實體。該狀態會致使一個插入操做。
Deleted 將一個實體標記爲刪除。設置該狀態時,該實體會從DbSet中移除。該狀態會致使刪除操做。
Detached DbContext再也不追蹤該實體。
Modified 自從DbContext開始追蹤該實體,該實體的一個或多個屬性已經更改了。該狀態會致使更新操做。
Unchanged 自從DbContext開始追蹤該實體以來,它的任何屬性都沒有改變。

 1,DbSet的Attach方法本質上是將實體的狀態設置爲Unchanged

var donator = new Donator
{
    Id = 4,
    Name = "雪茄",
    Amount = 18.80m,
    DonateDate = DateTime.Parse("2016/4/15 0:00:00")
};
using (var db = new DonatorsContext())
{
    db.Donators.Attach(donator);
    //db.Entry(donator).State=EntityState.Modified;//這句能夠做爲第二種方法替換上面一句代碼
    donator.Name = "秦皇島-雪茄";
    db.SaveChanges();
}

2,可使用RemoveRange方法刪除多個實體 

3,DbSet的Local屬性強制執行一個只針對內存數據的查詢

var query= db.Provinces.Local.Where(p => p.ProvinceName.Contains("")).ToList();

4,Find方法在構建數據庫查詢以前,會先去本地的上下文中搜索

5,經過ChangeTracker對象,咱們能夠訪問內存中全部實體的狀態

using (var db=new DonatorsContext())
{
    //14.1 證實Find方法先去內存中尋找數據
    var provinces = db.Provinces.ToList();
    //var query = db.Provinces.Find(3);//還剩Id=3和4的兩條數據了

    //14.2 ChangeTracker的使用
    foreach (var dbEntityEntry in db.ChangeTracker.Entries<Province>())
    {
        Console.WriteLine(dbEntityEntry.State);
        Console.WriteLine(dbEntityEntry.Entity.ProvinceName);
    }
}
#endregio

 

5、EF使用視圖

使用Database對象的另外一個方法SqlQuery查詢數據,該方法和ExecuteSqlCommand方法有相同的形參,可是最終返回一個結果集

var sql = @"SELECT DonatorId ,DonatorName ,Amount ,DonateDate ,ProvinceName from dbo.DonatorViews where ProvinceName={0}";
var donatorsViaCommand = db.Database.SqlQuery<DonatorViewInfo>(sql,"河北省");
foreach (var donator in donatorsViaCommand)
{
    Console.WriteLine(donator.ProvinceName + "\t" + donator.DonatorId + "\t" + donator.DonatorName + "\t" + donator.Amount + "\t" + donator.DonateDate);
}

SqlQuery方法的泛型參數不必定非得是一個類,也能夠.Net的基本類型,如string或者int

 

6、EF使用存儲過程

1,使用SqlQuery方法

using (var db=new DonatorsContext())
{
    var sql = "SelectDonators {0}";
    var donators = db.Database.SqlQuery<DonatorFromStoreProcedure>(sql,"山東省");
    foreach (var donator in donators)
    {
        Console.WriteLine(donator.ProvinceName+"\t"+donator.Name+"\t"+donator.Amount+"\t"+donator.DonateDate);
    }
}

假如要提供多個參數的話,多個格式化佔位符必須用逗號分隔,還要給SqlQuery提供值的數組

2,使用ExecuteSqlCommand方法

using (var db = new DonatorsContext())
{
    var sql = "UpdateDonator {0},{1}";
    var rowsAffected = db.Database.ExecuteSqlCommand(sql, "Update", 10m);
}

3,使用存儲過程CUD

EF Code First全面支持這些查詢。咱們可使用熟悉的EntityTypeConfiguration類來給存儲過程配置該支持,只須要簡單地調用MapToStoredProcedures方法就能夠了

    public class UserMap:EntityTypeConfiguration<User>
    {
        public UserMap()
        {
            //只須要簡單地調用MapToStoredProcedures方法就能夠了
            //MapToStoredProcedures();

            //自定義配置
            MapToStoredProcedures(config =>
            {
                //將刪除打賞者的默認存儲過程名稱更改成「DonatorDelete」,
                //同時將該存儲過程的參數名稱更改成「donatorId」,並指定該值來自Id屬性
                config.Delete(
                    procConfig =>
                    {
                        procConfig.HasName("UserDelete");
                        procConfig.Parameter(d => d.Id, "userId");
                    });

                //將默認的插入存儲過程名稱更改成「DonatorInsert」
                config.Insert(
                    procConfig =>
                    {
                        procConfig.HasName("UserInsert");
                    });
                //將默認的更新存儲過程名稱更改成「DonatorUpdate」
                config.Update(procConfig =>
                {
                    procConfig.HasName("UserUpdate");
                });
            });
        }
    }

 

7、異步API

例如,若是使用了異步的方式在建立一個Web應用,當咱們等待數據庫完成處理一個請求(不管它是一個保存仍是檢索操做)時,經過將web工做線程釋放回線程池,就能夠更有效地利用服務器資源

 1,異步地從數據庫中獲取對象的列表

//3.1 異步查詢對象列表
static async Task<IEnumerable<Donator>> GetDonatorsAsync()
{
    using (var db = new DonatorsContext())
    {
        return await db.Donators.ToListAsync();
    }
}  

2, 異步定位一條記錄

咱們能夠異步定位一條記錄,可使用不少方法,好比SingleFirst,這兩個方法都有異步版本。

//3.3 異步定位一條記錄
static async Task<Donator> FindDonatorAsync(int donatorId)
{
    using (var db = new DonatorsContext())
    {
      return await db.Donators.FindAsync(donatorId);
    }
}

3,異步聚合函數

對應於同步版本,異步聚合函數包括這麼幾個方法,MaxAsync,MinAsync,CountAsync,SumAsync,AverageAsync

//3.4 異步聚合函數
static async Task<int> GetDonatorCountAsync()
{
    using (var db = new DonatorsContext())
    {
        return await db.Donators.CountAsync();
    }
}

4,異步遍歷查詢結果

若是要對查詢結果進行異步遍歷,可使用ForEachAsync

//3.5 異步遍歷查詢結果
static async Task LoopDonatorsAsync()
{
    using (var db = new DonatorsContext())
    {
        await db.Donators.ForEachAsync(d =>
        {
            d.DonateDate=DateTime.Today;
        });
    }
}

 

8、管理併發

 1,爲併發實現RowVersion

RowVersion機制使用了一種數據庫功能,每當更新行的時候,就會建立一個新的行值

[Timestamp]
public byte[] RowVersion { get; set; }

//修改上下文

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Donator>().Property(d => d.RowVersion).IsRowVersion();
    base.OnModelCreating(modelBuilder);
}

如今,EF就會爲併發控制追蹤RowVersion列值。接下來嘗試更新不一樣的列:

try{
  //1.用戶甲獲取id=1的打賞者
  var donator1 = GetDonator(1);
  //2.用戶乙也獲取id=1的打賞者
  var donator2 = GetDonator(1);
  //3.用戶甲只更新這個實體的Name字段
  donator1.Name = "用戶甲";
  UpdateDonator(donator1);
  //4.用戶乙只更新這個實體的Amount字段
  donator2.Amount = 100m;
  UpdateDonator(donator2);   
}
catch (DbUpdateConcurrencyException ex)
{
    Console.WriteLine("發生併發更新");
}

 

9、事務

 1,EF的默認事務處理

int outputId = 2,inputId=1;
decimal transferAmount = 1000m;
using (var db=new Context())
{
    //1 檢索事務中涉及的帳戶
    var outputAccount = db.OutputAccounts.Find(outputId);
    var inputAccount = db.InputAccounts.Find(inputId);
    //2 從輸出帳戶上扣除1000
    outputAccount.Balance -= transferAmount;
    //3 從輸入帳戶上增長1000
    inputAccount.Balance += transferAmount;

    //4 提交事務
    db.SaveChanges();
}

2,使用TransactionScope處理事務

 int outputId = 2, inputId = 1;
 decimal transferAmount = 1000m;
 using (var ts=new TransactionScope(TransactionScopeOption.Required))
 {
     var db1=new Context();
     var db2=new Context();
     //1 檢索事務中涉及的帳戶
     var outputAccount = db1.OutputAccounts.Find(outputId);
     var inputAccount = db2.InputAccounts.Find(inputId);
     //2 從輸出帳戶上扣除1000
     outputAccount.Balance -= transferAmount;
     //3 從輸入帳戶上增長1000
     inputAccount.Balance += transferAmount;

     db1.SaveChanges();
     db2.SaveChanges();

     ts.Complete();
 }

3,使用EF6管理事務

 int outputId = 2, inputId = 1;
 decimal transferAmount = 1000m;
 using (var db=new Context())
 {
     using (var trans=db.Database.BeginTransaction())
     {
         try
         {
             var sql = "Update OutputAccounts set Balance=Balance-@amountToDebit where id=@outputId";
             db.Database.ExecuteSqlCommand(sql, new SqlParameter("@amountToDebit", transferAmount), new SqlParameter("@outputId",outputId));

             var inputAccount = db.InputAccounts.Find(inputId);
             inputAccount.Balance += transferAmount;
             db.SaveChanges();

             trans.Commit();
         }
         catch (Exception ex)
         {
             trans.Rollback();
         }
     }
 }

4,使用已存在的事務

//模擬老項目的類庫
static bool DebitOutputAccount(SqlConnection conn, SqlTransaction trans, int accountId, decimal amountToDebit)
{
    int affectedRows = 0;
    var command = conn.CreateCommand();
    command.Transaction = trans;
    command.CommandType=CommandType.Text;
    command.CommandText = "Update OutputAccounts set Balance=Balance-@amountToDebit where id=@accountId";
    command.Parameters.AddRange(new SqlParameter[]
    {
        new SqlParameter("@amountToDebit",amountToDebit), 
        new SqlParameter("@accountId",accountId) 
    });

    try
    {
        affectedRows= command.ExecuteNonQuery();
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return affectedRows == 1;
}
    #region 7.0 使用已存在的事務
    int outputId = 2, inputId = 1;
    decimal transferAmount = 1000m;
    var connectionString = ConfigurationManager.ConnectionStrings["ConcurrencyAndTransactionManagementConn"].ConnectionString;
    using (var conn=new SqlConnection(connectionString))
    {
        conn.Open();
        using (var trans=conn.BeginTransaction())
        {
            try
            {
                var result = DebitOutputAccount(conn, trans, outputId, transferAmount);
                if (!result)
                {
                    throw new Exception("不能正常扣款!");
                }
                using (var db=new Context(conn,contextOwnsConnection:false))
                {
                    db.Database.UseTransaction(trans);
                    var inputAccount=db.InputAccounts.Find(inputId);
                    inputAccount.Balance += transferAmount;
                    db.SaveChanges();
                }
                trans.Commit();
            }
            catch (Exception ex) 
            {
                trans.Rollback();
            }
        }
    }

    #endregion

db.Database.UseTransaction(trans):這句話的意思是,EF執行的操做都在外部傳入的事務中執行

contextOwnsConnection的值爲false:表示上下文和數據庫鏈接沒有關係,上下文釋放了,數據庫鏈接還沒釋放;反之爲true的話,上下文釋放了,數據庫鏈接也就釋放了

5,選擇合適的事務管理

目前,咱們已經知道了好幾種使用EF處理事務的方法,下面一一對號入座:

  • 若是隻有一個DbContext類,那麼應該盡力使用EF的默認事務管理。咱們總應該將全部的操做組成一個在相同的DbContext對象的做用域中執行的工做單元,SaveChanges()方法會處理提交事務。
  • 若是使用了多個DbContext對象,那麼管理事務的最佳方法可能就是把調用放到TransactionScope對象的做用域中了。
  • 若是要執行原生SQL,並想把這些操做和事務關聯起來,那麼應該使用EF提供的Database.BeginTransaction()方法。然而這種方法只支持EF6,不支持以前的版本。
  • 若是想爲要求SqlTransaction的老項目使用EF,那麼可使用Database.UseTransaction()方法,在EF6中可用。

 

10、數據庫遷移

1,咱們要給Message屬性添加一個限制,即最大長度爲50(默認的長度是MAX),而後更新數據庫,結果會報錯

產生這個錯誤的道理很簡單,字符串長度從最大變成50,確定會形成數據丟失的。若是你知道會形成數據丟失,還要這麼作,能夠在後面加參數-Force,這個參數會強制更新數據庫。或者,咱們能夠開啓數據丟失支持,正如EF暴露的這個設置Set AutomaticMigrationDataLossAllowed to 'true'(錯誤信息中提到的)。

 

2,UpDown方法

Up方法將數據庫結構向前移動,例如,咱們這裏建立了兩張新的表;Down方法幫助咱們撤銷更改,以防咱們發現了軟件問題須要回滾到以前的數據庫結構

 

3,__MigrationHistory表

__MigrationHistory這張表,顧名思義,是記錄遷移歷史的。能夠看到,MigrationId對應於初次遷移的文件名,Model列包含了上下文的哈希值,ContextKey包含了上下文配置類的類名

 

4,defaultValueSql

要指定硬編碼默認值,咱們可使用defaultValue

 public override void Up()
 {
     AddColumn("dbo.Donators", "CreationTime", c => c.DateTime(nullable: false,defaultValueSql:"GetDate()"));
 }

上面的代碼中,咱們使用了SQL Server中的GetDate函數使用當前日期填充新加入的列

 

5,要建立遷移,咱們不必定非要有未處理的更改。咱們仍然使用以前的指令Add-Migration,這樣就給項目添加了一個遷移,可是Up和Down方法都是空的。如今,咱們須要添加建立索引的自定義的代碼,以下所示

public partial class Donator_Add_Index_Name : DbMigration
{
    public override void Up()
    {
        CreateIndex(
            "Donators",
            new []{"Name"},
            name:"Index_Donator_Name"
            );
    }
    
    public override void Down()
    {
        DropIndex("Donators", "Index_Donator_Name");
    }
}

 

6,回滾

如今,嘗試經過指定建立索引遷移以前的目標遷移刪除該索引,建立索引以前的遷移名稱是Donator_Add_CreationTime,要退回上一個遷移,咱們可使用下面的指令:

Update-Database -TargetMigration Donator_Add_CreationTime 

 

11、應用遷移

 1,經過腳本應用遷移

包管理器控制檯窗口中,咱們能夠經過Update-Database -Script生成腳本,該命令一執行完成,生成的腳本就會在VS中打開

須要注意的是,咱們要指定匹配目標環境的數據庫的正確鏈接字符串,由於遷移API會使用上下文比較實時數據庫。咱們要麼在Update-Database後面帶上鍊接字符串,要麼在配置文件中使用正確的鏈接字符串。

 

2,給已存在的數據庫添加遷移

有時,咱們想爲一個已存在的數據庫添加EF遷移,爲的是將處理模式變化從一種方式移動到遷移API。固然,由於數據庫已存在於生產環境,因此咱們須要讓遷移知道遷移起始的已知狀態。使用Add-Migration -IgnoreChanges指令處理這個是至關簡單的,當執行該命令時,EF會建立一個空的遷移,它會假設上下文和實體定義的模型和數據庫是兼容的。一旦經過運行這個遷移更新了數據庫,數據庫模式不會發生變化,可是會在_MigrationHistory表中添加一條新的數據來對應初次遷移。這個完成以後,咱們就能夠安全地切換到EF的遷移API來維護數據庫模式變化了。

 

12、EF的其餘功能

1, 應用於不少實體類型或者表的全局更改(好比,下面是如何設置全部的string屬性在數據庫中存儲爲非Unicode列)

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<string>().Configure(config=>config.IsUnicode(false));
}

咱們也能夠寫相同的代碼做爲自定義約定,而後在model builder中加入到約定集合中。要這麼作的話,首先建立一個繼承自Convention的類,重寫構造函數,而後使用以前相同的代碼,調用Convention類的Properties方法。代碼以下:

public class CustomConventions:Convention
{
    public CustomConventions()
    {
        Properties<string>().Configure(config=>config.IsUnicode(false));
    }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    //modelBuilder.Properties<string>().Configure(config=>config.IsUnicode(false));
    modelBuilder.Conventions.Add<CustomConventions>();
}

 

2,地理空間數據

除了標量類型數據,如string或decimal,EF也經過.Net中的DbGeometryDbGeography支持地理空間數據。這些類型有支持地理空間查詢的內置支持和正確翻譯,例如地圖上兩點之間的距離。這些特定的查詢方法對於具備地理空間屬性的實體的查詢頗有用,換言之,當使用空間類型時,咱們仍編寫.Net代碼。

 

----------------------------------------------------------------------------------------

Local只能跟蹤CUR操做。Local只能針對某一個DbSet而言
ChangeTracker能夠跟蹤整個DBContext
Entry只能跟蹤某個一實體

繼承IDbCommonInterceptor進行攔截EF執行

Timestamp特性標記字段爲時間戳(該字段類型必須是byte[])
ConcurrencyCheck特性併發檢測
NotMapped特性:使該字段不生成數據庫字段

Codefirst四大初始化策略
Database.SetInitializer<Entity>(new DropCreateDatabaseIfModelChanges<Entity>())

1,CreateDatabaseIfNotExists:默認策略
①數據庫不存在,建立數據庫
②model修改,執行拋出異常


2,DropCreateDatabaseIfModelChanges:
model一旦修改,咱們將會執行dropdatabase操做

3,DropCreateDatabaseAlways:
每一次執行就從新建立表

4,Customer DB Initializer:
自定義初始化


5,禁用數據庫初始化策略
Database.SetInitializer<Entity>(null)

使用EFProf工具監視ef支持的sqlhttps://www.codeproject.com/Articles/101214/EFProf-Profiler-Tool-for-Entity-Framework

相關文章
相關標籤/搜索