参照文档: http://www.cnblogs.com/farb/p/ABPAdvancedTheoryContent.htmlhtml
案例:http://pan.baidu.com/s/1c1Qgg28web
3、预加载数组
4、CURD安全
5、EF使用视图服务器
7、异步API并发
8、管理并发app
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, 异步定位一条记录
咱们能够异步定位一条记录,可使用不少方法,好比Single
或First
,这两个方法都有异步版本。
//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处理事务的方法,下面一一对号入座:
SaveChanges()
方法会处理提交事务。TransactionScope
对象的做用域中了。Database.BeginTransaction()
方法。然而这种方法只支持EF6,不支持以前的版本。SqlTransaction
的老项目使用EF,那么可使用Database.UseTransaction()
方法,在EF6中可用。
10、数据库迁移
1,咱们要给Message属性添加一个限制,即最大长度为50(默认的长度是MAX),而后更新数据库,结果会报错
产生这个错误的道理很简单,字符串长度从最大变成50,确定会形成数据丢失的。若是你知道会形成数据丢失,还要这么作,能够在后面加参数-Force
,这个参数会强制更新数据库。或者,咱们能够开启数据丢失支持,正如EF暴露的这个设置Set AutomaticMigrationDataLossAllowed to 'true'(错误信息中提到的)。
2,Up
和Down
方法
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中的DbGeometry
和DbGeography
支持地理空间数据。这些类型有支持地理空间查询的内置支持和正确翻译,例如地图上两点之间的距离。这些特定的查询方法对于具备地理空间属性的实体的查询颇有用,换言之,当使用空间类型时,咱们仍编写.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