书名:你必须掌握的EntityFramework 6.x与Core 2.0
【重要】EF开发技巧
- 当EF编写复杂查询时,EF和Linq不是很好的方案,使用纯Sql效率会更高;
- 推荐多种数据库访问工具一起开发,如直接用Sql的查询可以使用更轻量级的Dapper实现;
- EF的延时加载是把双刃剑,在很多情况下,采用即时加载(Include)能极大的提高数据操作效率,如批量导入,可以采用及时加载的形式;
- EF另一个容易引发性能问题的点是修改跟踪对象的状态,所以EF对象集合不应该当成普通的对象集合,如果是批量添加数据先加到一个普通集合在通过AddRange加到EF对象集合上;
- 大佬连接: Arthur Blog
- 大佬链接:Jeffcky
- 微软官方文档:https://docs.microsoft.com/zh-cn/ef/ef6/querying/local-data
- 实际上一直自己开关状态监测,对开发人员是一件非常不爽的事情,也失去一部分使用EF的乐趣,一般简单的处理方式是,查询和向DTO或UI输出的实体,直接加上AsNoTracking(),如果需要业务处理,在NoTracking状态下,做好实体的各种变更,再Attach()回来,手动修改一下EntryState会避免很多不必要的麻烦;
问题列表
- .Net Standard与.Net Framework的区别于联系;
- EF Core比EF性能更高?
- EF中dbContext.GetValidationErrors()的作用;
- modelBuilder.ComplexType<Address>();干什么的?
- EF中的对context.Entry<T>().Load()与context.Entry<T>().ToList()的区别?
- EF实体查询中,SingleOrDefault()与FirstOrDefault()还有Find的区别?
- 数据库中的表值函数是什么(who),有什么用(what),怎么用(how)?
- 5W2H分析法
笔记
- Context中如果只读可以写为:
public DbSet<Blog> Blogs{ get {return Set<Bolg>();} }
- EF初始化数据库策略有三种:不存在创建、总是创建新数据库、如果模型发生改变更新数据库模型;可以通过
Database.SetInitializer(...) //如果传null怎不进行执行数据库初始化策略
,也可以通过App.config进行配置 - 表主键如果不指定,默认只能是类名称+ID或ID,不然EF会提示未设置主键;
- 除了定义导航属性外,建议在依赖对象上定义外键属性:
public class Department
{
public int DepartmentID {get; set;}
public virtual ICollection<Course> Courses{get; set;}
}
public class Course
{
public int CourseID{get; set;}
public DepartmentID{get; set;}
public virtual Department Department{get; set;}
}
- 复杂类型的实体(如实体中存在内部类),建议与实体类映射在一个表中:
public class Order
{
public int OrderID{get; set;}
public string OrderName{get; set;}
public class Address
{
public string Street{get; set;}
public ...
}
}
----------------------------------------------
modelBuilder.Entry<Order>().ToTable("Orders");
modelBuilder.ComplexType<Address>();
---------------------------------------------
[Table]Orders
OrderID
OrderName
Street
...
- 自定义约定
//将所有实体中类型为DateTime的类型在数据库中映射为datetime2类型
public class DateTime2Convention:Convention
{
public DateTime2Convention()
{
this.Property<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
-------------------------------------------------------------------------------
modelBuilder.Conventions.Add(new DateTime2Convention());
//还可以使用modelBuilder.Conventions.AddBefore<T>(new DateTime2Convention());在T约定之前执行
//AddAfter是添加到某个约定之后执行;
还可使用配置直接约定
modelBuilder.Properties<string>().Where(x => x.Name == "Name").Configure(c => c.HasMaxLength(200));
自定义特性进行约定
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NonUnicodeAttribute : Attribute
{
public bool Unicode { get; set; }
public NonUnicodeAttribute(bool isUnicode)
{
Unicode = isUnicode;
}
}
----------------------------------------------------------------------------------------------
//类似于过滤器
modelBuilder.Properties().Having(p => p.GetCustomAttributes(false).OfType<NonUnicodeAttribute>().FirstOrDefault()).Configure((con, att) => con.IsUnicode(att.Unicode));
利用配置约定代替继承关系表中的Discriminator(TPH策略下,如果不指定,EF默认增加的列)列
modelBuilder.Entity<EntityBase>().Map<SubEntity1>(s => s.Requires("EntityType").HasValue(0)).Map<SubEntity2>(s => s.Requires("EntityType").HasValue(1));
- 自定义配置
//自定义配置
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
//SetProviderServices(...);
}
}
----------------------------------------------------------------------
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlConfiguration))]
public class MysqlContext : SqlServerContext
...
- 特殊的Fluent API
//HasDatabaseGeneratedOption配置数据库是否自增;None:不设置值,Identity:插入生成值,Computed:插入或修改就会生成值
modelBuilder.Entity<T>().HasKey(t => t.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
//设置精度
modelBuilder.Entity<T>().Property(p => p.Length).HasPrecision(18,2);
//不可空
modelBuilder.Entity<T>().Property(p => p.Name).IsRequired();
//可空
modelBuilder.Entity<T>().Property(p => p.Name).IsOptional();
//映射数据库对于的类型
modelBuilder.Entity<T>().Property(p => p.Name).HasColumnType("VARCHAR");
//修正
modelBuilder.Entity<T>().Property(p => p.Name).IsFixedLength();
//设置为复杂类型,复杂类型不可延迟加载
modelBuilder.ComplexType<ComplexA>();
//Token乐观并发检测
modelBuilder.Entity<T>().Property(p => p.Name).IsConcurrencyToken();
//行版本乐观迸发检测
modelBuilder.Entity<T>().Property(p => p.Name).IsRowVersion();
//建立索引
modelBuilder.Entity<T>().Property(p => p.Name).HasColumnAnnotation("Index", new IndexAnnotation(new []{
new IndexAttribute("Index1"),
new IndexAttribute("Index2"){IsUnique = true}
}));
modelBuilder.Entity<T>().Property(p => p.Name).HasColumnAnnotation("Index", new IndexAttribute());
//建立复合索引见10小标题
//存储过程
modelBuilder.Entity<T>().MapToStoredProcedures();
- Context中用到的方法
//获取实体EntityA中复杂类型ComplexObj的ComplexPropertyA属性的原始值
var ComplexPropertyA = context.Entry(EntityA).ComplexProperty(e => e.ComplexObj).Property(c => c.ComplexPropertyA).OriginalValue;
- 实体配置类,需要继承EntityTypeConfiguration
public class EntityMap:EntityTypeConfiguration<Entity>
{
public EntityMap()
{
//index
HasIndex(e => e.Name);
HasIndex(e => e.No);
//复合索引
HasIndex(e => new {Name = e.Name, No = e.No});
//配置HasOptional与WithOptionalPrincipal(Principal:主要的)
HasOptional(e => e.EntityRelation).WithOptionalPrincipal(r => r.Entity);//外键在EntityRelation中
//配置HasOptional与WithOptionalDependent(Dependent:主要的)
HasOptional(e => e.EntityRelation).WithOptionalDependent(r => r.Entity);//外键在Entity中
//配置HasRequired与WithRequiredPrincipal(Principal:主要的)
HasRequired(e => e.EntityRelation).WithRequiredPrincipal(r => r.Entity);//外键在EntityRelation中
//配置HasRequired与WithRequiredDependent(Dependent:主要的)
HasRequired(e => e.EntityRelation).WithRequiredDependent(r => r.Entity);//外键在Entity中
}
}
- 映射的三种模式TPH、TPT、TPC
TPH:Table per Hierarchy,将多个类放到同一个表中,用discriminator区分类型,继承常用
THT:Table per Type,用外键表示继承关系
THC:Table per Concreate class,最不被推荐的,继承关系中,每个子表会包含所有父类的字段,相当于很多数据表数据字段是重复的modelBuilder.Entity<T>().Table("T");
modelBuilder.Entity<T>().Map(t => { t.MapInheritedProperties(); t.ToTable("T");
});
```
12. 实体分开存放到不同数据表中
public class Entity
{
public int Id{get; set;}
public string Name{get; set;}
public string Property1{get; set;}
public string Property2{get; set;}
}
public class EntityMap:EntityTypeConfiguration<Entity>
{
public EntityMap()
{
Map(map => {
map.Properties(p =>new {
p.Id,
p.Name,
p.Property1
});
map.ToTable("SubEntity1");
}).Map(map2 => {
map2.Properties(p =>new {
p.Property2
});
map2.ToTable("SubEntity2");
});
}
}
----------------------------------------------------------
[Table]SubEntity1
Id
Name
Property1
[Table]SubEntity2
Id
Property2
- 将数据表合并为一个实体POCO类
public class Entity
{
public int Id{get; set;}
public string Name{get; set;}
public virtual EntityRelation Relation{get; set;}
}
public class EntityRelation
{
public int RelationId{get; set;}
public string Property1{get; set;}
public Entity Entity{get; set;}
}
public class EntityMap:EntityTypeConfiguration<Entity>
{
public EntityMap()
{
ToTable("Entity");
HasKey(e => e.Id);
HasRequired(e => e.Relation).WithRequiredPrincipal(r => r.Entity);
}
}
public class EntityRelationMap:EntityTypeConfiguration<EntityRelation>
{
public EntityRelationMap()
{
ToTable("Entity");
HasKey(e => e.RelationId);
}
}
-----------------------------------------------------------------
[Table]Entity
Id
Name
Property1
- Cast与OfType都可以转换类型,但是OfType是在数据库中查询,Cast都查询出来进行转换;
- EF加载数据方式有三种:延迟加载、饥饿加载、显示加载
饥饿加载(Eager Loading):Project project = context.Projects.Include("Standard").FirstOrDefault();
显示加载(Explicitly Loading):
context.Entry(project).Collection(p => p.Standards).Load();
或context.Entry(project).Collection(p => p.Standards).Query().ToList();
- EF上下文使用Sql查询:
不会跟踪:context.Database.SqlQuery<Entity>("sql command", params object[] parameters)
,支持只查某个字段或某些与自定义的字段;
上下文跟踪:context.Entities.SqlQuery("sql command", params object[] parameters)
或context.Set<Entity>().SqlQuery("sql command", params object[] parameters)
,sql语句查询的内容必须符合Entity,不能只查某些,比如只查Id; - EF sql增删改:
context.Database.ExecuteSqlCommand(sql, parameters);
- EF context更新实体的方式:
实体属性全部更新:
context.Entry(entity).State = EntityState.Modified
部分属性发生变化://方法1:变更的属性注意给数据库对象赋值 dbEntity.Name = updateEntiy.Name; //方法2:采用EF自己提供的Set方法 context.Entry(dbEntity).CurrentValues.SetValues(updateEntity); if(!context.ChangeTracker.HasChanges()) { //更新成功 } else if(context.SaveChanges() > 0) { //更新成功 }else { //更新失败 }
- EF批量更新,可以使用EntityFramework.Extended插件来实现
context.Entities.Where(e => e.Name == "abc").Update(e => new Entity(){Property1 = "updateProperty1"});
context.SaveChanges();
- EF 自动创建存储过程,如果不想通过Add或Remove来操纵实体,可以使用自动生成的存储过程操作实体
public class EntityMap : EntityTypeConfiguration<Entity>
{
public EntityMap()
{
//在迁移中会自动生成增删改的三个存储过程
MapToStoredProcedures();
}
}
--------------------------------------------------------------
//使用
context.Database.ExecuteSqlCommand("dbo.Entity_update @Name", parameters);
- EF中使用
Configuration.UseDatabaseNullSemantics=true;
来生成更简单的查询语句; - 表值函数在EF中的调用
context.Database.SqlQuery<Entity>("Select * From [dbo].[GetEntities]()");
- EF中对日期操作
//不能直接用DateTime.Now - e.ModifyTime
context.Entities.Select(e => new { Time = SqlFunctions.DateDiff("DAY", e.ModifyTime, DateTime.Now)}).ToList();
- 使用AutoFac、NinjectMvc管理Context的生命周期
- 快照是变更跟踪,使用
Configuration.AutoDetectChangesEnabled= true
来开启,开启后在修改属性时会调用DetectChanges,而context.Entry会在开启AutoDetectChangesEnabled时调用DetectChanges,快照式变更利用了POCO的快照相关行为; - 代理式变更追踪,需要加virtual,使用
Configuration.ProxyCreationEnabled = true
来开启,生成EF的代理类来通知virtual的属性变更,所以在AutoDetectChangesEnabled = false; 加virtual的属性变更也能被发现;
var obj = ((IObjectContextAdapter)context).ObjectContext;
//创建Entity的代理类,entity的类型为Entity在EF中的代理类
var entity = obj.CreateObject<Entity>();
-
延迟加载必须满足三个条件
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
导航属性必须为virtual; -
DetectChange
用途
修改实体之前的引用以及实体内部的状态和索引。如果上下文中跟踪的成千上万实体,更新会非常占资源与消耗性能,如果尝试优化自调用DetectChanges就不用担心潜在的问题了。调用时机
DbSet.Find
DbSet.Local
DbSet.Remove
DbSet.Add
DbSet.Attach
DbContext.SaveChanges
DbContext.GetValidationErrors
DbContext.Entry
DbChangeTracker.Entries何时关闭DetectChange
a.不建议关闭自动调用DetectChanges;
b.如果想关闭DetectChanges在局部try/finally中完成;
c.使用DbContext中的API来完成实体更改,无需调用DetectChanges。关闭DetectChange方式
try { context.Configuration.AutoDetectChangesEnabled = false; //update } finally { context.Configuration.AutoDetectChangesEnabled = true; } context.SaveChanges();
//Property与Reference在AutoDefectChangesEnabled = false也能更新 try { context.Configuration.AutoDefectChangesEnabled = false; //p在执行到这里之前不能被跟踪到上下文中 context.Projects.Attach(p); context.Entry(p).Property(p2 => p2.Name).CurrentValue = "updateName"; context.Entry(p).Reference(p2 => p2.No).CurrentValue = "updateNo"; context.SaveChanges(); } finally { context.Configuraton.AutoDetectChangeEnabled = true; }
二进制属性与复杂类型
1.只能通过重新new的方式给这两种属性更新
2.复杂复杂类型也可以通过context的API进行更新 -
EF Sql打印
//ToString
var sql = context.Customs.ToString();
//context.Database.Log
context.Database.Log = Console.WriteLine;
//to disk
var stream = new StreamWriter("path"){AutoFlush = true};
context.Database.Log = (s) => stream.WriteLine(s);
//结构化日志输出
//DatabaseLogFormatter
public class MyLog : DatabaseLogFormatter
{
public override void LogCommand<T>(...){}
public override void LogResult<T>(...){}
public override void LogParamater...
}
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
SetDatabaseLogFormatter((context, action) => new MyLog(context, action));
}
}
====================================
using(var context = new MyContext)
{
context.Database.Log = s => Console.WriteLine(s);
}
//构造块拦截
//1.使用NLog与NLog.Config两个日志框架进行拦截
//2.实现IDbInterceptor接口
public class MyInterceptor : IDbInterceptor{}
//3.添加到EF拦截器配置中
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
DbInterception.Add(new MyInterceptor());
}
}
//Ef也可以在App.config中增加interceptors节点来注册拦截器,进行拦截
<interceptors>
<interceptor type="命名空间.DatabaseLoger, EntityFramework">
<paramaters>
<paramater value="log path"/>
<paramater value="true" type="System.Boolean"/>
</paramaters>
</interceptor>
</interceptors>
//拦截器异常性能监控
//1.IDbCommandInterceptor 耗时性能监控
//2.DbCommandInterceptor 耗时性能监控
https://www.cnblogs.com/lgxlsm/p/6305816.html
//3.拦截器可以解决EF与SqlServer对字符串首尾空格处理不一致的问题
- 事务
事务默认不在查询中使用;
SaveChanges默认会创建一个事务;
EF事务的默认隔离级别为ReadCommitted;
EF中context.Database.ExecuteSqlCommand默认是启用事务的,可以通过参数关闭事务包裹;
在DbContext实现类中可以通过Configuration.EnsureTransactionsForFunctionsAndCommands来控制是否启用默认事务,但是此配置不能控制SaveChanges内部启用事务;
EF事务支持的三种模式:DbContextTransaction,DbTransaction,TransactionScope
//1.DbContextTransaction,仅适用于Context
using(var dbContextTransaction = context.Database.BeginTranscation())
{}
//2.DbTransaction即使用context.Database.UseTransaction(...),仅适用于底层连接
//如果想使用context.Database.UseTransaction(...)必须满足三个条件:①上下文中关闭EF连接,使用底层连接;②使用ado启动连接,ado连接上启动Transaction,context使用ado启动的连接(即使用了底层的Transaction);③使用context.Database.UseTransaction(trans)统一使用底层的Transaction。
public class MyContext: DbContext
{
public MyContext(string strConn): base(strConn, contextOwnsConnection:false)
{}
}
=================================
using(var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
conn,Open();
using(var trans = conn.BeginTransaction())
{
try
{
using(var context = new MyContext(conn))
{
context.UseTransaction(trans);
//...
}
trans.Commit();
}catch(exception ex)
{
trans.Rollback();
}
}
}
//3.TransactionScope
//TransactionScopeOption可以配置事务管理器需要参加哪些事务,有三个选项
//1>Required:必须要有事务,如果事务已经存在则使用已存在的事务,不存在则创建事务;
//2>RequiresNew:始终为上下文实例创建新的事务;
//3>Suppress:创建上下文实例时,如果存在其他事务,其他事务将不起作用,在此事务范围内的上下文操作完成与其他事务无关,抑制事务的影响,相当于没有事务的约束,https://docs.microsoft.com/en-us/previous-versions/ms172152(v=vs.90)?redirectedfrom=MSDN#Y1642。
using(TransactionScope scope1 = new TransactionScope())
//Default is Required
{
using(TransactionScope scope2 = new
TransactionScope(TransactionScopeOption.Required))
{
...
}
using(TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
...
}
using(TransactionScope scope4 = new
TransactionScope(TransactionScopeOption.Suppress))
{
...
}
scope1.Complete();
}
分布式事务处理协调器(DTC)服务,该服务协调更新两个或多个受保护资源的事务,例如数据库、消息队列、文件系统等。打开方式:控制面板->管理工具->组件服务->Distributed Transaction Coordinator进行查看事务提交信息,在电脑DTC右键进行配置。
- 数据库弹性连接(重试机制,Polly治理)
EF6提供的四种连接策略:
①DefaultExecutionStrategy:只有SQLServe会重试;
②DefaultSqlExecutionStrategy:不会重试,但会抛出异常提醒开发者;
③DbExecutionStrategy:可以以此类为基础自定义重试机制,默认重试0次,重写ShouldRetryOn方法,来控制重试哪些异常;
④SqlAzureExecutionStrategy:继承自③,将重试SqlAzure出现的已知异常。public class MySqlConfiguration : DbConfiguration { public MySqlConfiguration() { SqlExecutionStrategy(SqlProviderServices.ProviderInvariantName, () => new SqlAzureExecutionStrategy()); } }
Polly是非常强大的弹性和瞬态故障处理库,可以实现重试、断路、超时、故障恢复等7中策略。
https://www.cnblogs.com/CreateMyself/p/7589397.html#!comments
public class CirtuitBreakerExecutionStrategy : IDbExecutionStrategy
{
private Policy _policy;
public CirtuitBreakerExecutionStrategy(Policy policy)
{
_policy = policy;
}
public void Execute(Action operation)
{
_policy.Execute(() =>
{
operation.Invoke();
});
}
public TResult Execute<TResult>(Func<TResult> operation)
{
return _policy.Execute(() =>
{
return operation.Invoke();
});
}
public async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken)
{
await _policy.ExecuteAsync(() =>
{
return operation.Invoke();
});
}
public async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken)
{
return await _policy.ExecuteAsync(() =>
{
return operation.Invoke();
});
}
public bool RetriesOnFailure { get { return true; } }
}
public class EFConfiguration : DbConfiguration
{
public Policy _policy;
public EFConfiguration()
{
_policy = Policy.Handle<Exception>().CircuitBreaker(3, TimeSpan.FromSeconds(60));
SetExecutionStrategy("System.Data.SqlClient", () => new CirtuitBreakerExecutionStrategy(_policy));
}
}
- EF并发处理
① Token: Property(p => p.Name).IsConcurrencyToken();
② 行版本:public byte[] RowVersion{get; set;} ==> Property(p => p.RowVersion).IsRowVersion();
//并发异常常用的分析信息
cache(DbUpdateConcurrencyException ex)
{
//发生并发异常时被追踪的实体
//其中ex.Entries记录了所有冲突实体的跟踪信息
var trackingEntity = ex.Entries.Single();
//当前跟踪实体认为的原始值
var originEntity = trackingEntity.OriginalValues.ToObject();
//当前跟踪实体在数据库中的最新值
var databaseEntity = trackingEntity.GetDatabaseValues().ToObject();
//当前跟踪实体在内存中的值
var currentEntity = trackingEntity.CurrentValues.ToObject();
}
===================================================================
//如果想让所有修改都成功,可以如下操作
cache(DbUpdateConcurrencyException ex)
{
var trackingEntity = ex.Entries.Single();
trackingEntity.OriginalValues.SetValue(trackingEntity.GetDatabaseValues());
context.SaveChanges();
}
可以利用SaveChanges的拓展方法来处理并发冲突
https://gitee.com/EverestVIP/EntityFramework6.x-And-Core2.0/tree/EFCoreConcurrency
- app.config中设置连接字符串,增加"MultipleActiveResultSets=True;"可以是实现单连接多请求。
- 解决EF性能差的问题:https://blog.csdn.net/csdnnews/article/details/100111387
- 使用AsNoTracking()可以解决查询多,效率低的问题。
- 实体缓存
using (var context = new SqlServerContext())
{
var entity = context.ReadOnlyEntities.FirstOrDefault(e => e.ID.ToString() == "996eb5e5-2e32-49ca-a698-24c55f184a89");
entity.Name = "wxf1";
var entity1 = context.ReadOnlyEntities.FirstOrDefault(e => e.Name == "wxf");
//res = true;
var res = object.ReferenceEquals(entity, entity1);
}
- EF查询中FirstOrDefault/SingleOrDefault/Find
数据库中存在两条Name="wxf"的数据
===========================================
using (var context = new SqlServerContext())
{
//找数据库中不存在的数据,都不会报异常
try
{
//每次都会去数据库查
var entity = context.ReadOnlyEntities.FirstOrDefault(e => e.Name == "wxf");
}
catch (Exception)
{
Console.WriteLine("FirstOrDefault异常");
}
try
{
//每次都会去数据库查2条数据,Top(2),保证数据唯一性,如果发现多条数据会报异常
var entity1 = context.ReadOnlyEntities.SingleOrDefault(e => e.Name == "wxf");
}
catch (Exception)
{
Console.WriteLine("SingleOrDefault异常");
}
try
{
//每次都会去数据库查2条数据,Top(2),如果查询过则不会在去查询,保证数据唯一性,如果发现多条数据会报异常
var entity2 = context.ReadOnlyEntities.Find(Guid.Parse("996eb5e5-2e32-49ca-a698-24c55f184a80"));
}
catch (Exception)
{
Console.WriteLine("Find异常");
}
}
========================================
打印:SingleOrDefault异常
- 翻译缓存
·EF转为LINQ To Entity需要两部:①将linq表达式树翻译为数据库表达式树;②将数据库表达式树翻译为Sql;
·为了提高性能EF会将查询缓存在Microsoft.Extensions.Caching.Memory.MemoryCache中;
·在处理Lin去查询之前,EF会计算缓存秘钥,如果找到对于的缓存,则不进行翻译,直接使用缓存中翻译好的Sql进行查询;
根据上述三点得出尽量构造翻译缓存中存在的信息进行操作
//1.使用值类型变量给表达数赋值,而不是直接放到表达式树中
int num = 3;
context.Entities.FirstOrDefault(e => e.Num == num);
//尽量避免下边的写法
context.Entities.FirstOrDefault(e => e.Num == 3);
//2.Skip与Take使用使用lambda进行赋值
//正确写法
int offset = 0;
int count = 20;
context.Entities.OrderBy(e => e.Id).Skip(() => offset).Take(() => count).ToList();
//尽量避免的写法
context.Entities.OrderBy(e => e.Id).Skip(offset).Take(count).ToList();
这里推荐的一个检测查询语句执行次数的工具:Red Gate Profiler
- 重新编译查询
可以利用拦截器实现拦截sql,重新编译,另外,拦截器打印sql的用途:https://www.cnblogs.com/Ax0ne/p/3620958.html
var entity = new Entity(){Name = "abc", Address = null};
//查询1
var query = context.Entities;
if(!string.IsNullOrEmpty(entity.Name))
query = query.Where(e => e.Name == entity.Name);
if(!string.IsNullOrEmpty(entity.Address)) //不会进去
query = query.Where(e => e.Address == entity.Address );
query.ToList();
//查询2--------------------------------------------------------
var query2 = context.Entities.Where(e => (string.IsNullOrEmpty(entity.Name) || e.Name == entity.Name) && (string.IsNullOrEmpty(entity.Address) || e.Address == entity.Address)).ToList();
//查询1与查询2翻译为sql语句差异很大,所以可以使用上述拦截器对Sql进行重新编译查询
- EF N + 1查询优化
如果使用POCO对象时,关联集合对象没有Include加载,在使用关联集合时,每次都会到数据库查询关联集合数据;
解决办法:
①查询POCO对象时,所有用到的延时加载数据都要Include加载;
②使用DTO 与 POCO进行映射,映射工具推荐使用AutoMapper。 - EF添加索引可以提高效率
- 批量插入可以使用SQLBulkCopy提高性能,只适用于SQLserver
如果不使用SqlBulkCopy,可以使用AddRange + (AutoDetectChangesEnabled = false)进行效率提升,AutoDetectChangesEnabled 可以放到try/finally块里操作; - 并发情况可以使用异步操作:XXXAsync()方法
- EF实战搭建可以使用:工作单元泛型仓储模式、依赖注入泛型仓储模式。
- EF6迁移遵循规则 【见详细迁移】
对于EF的迁移我们要记住以下三点
①Entity Framework 实体框架使用_MigrationHistory表来追踪应用于数据库的更改
②Entity Framework 实体框架创建项目中当前模型状态的哈希值,并将其与_MigrationHistroy表中的模型状态存储进行比较,以确定数据库是否为当前状态
③进行迁移时,始终按照时间戳(升序)的顺序同步到数据库