EF读书心得

7 篇文章 0 订阅
5 篇文章 0 订阅

书名:你必须掌握的EntityFramework 6.x与Core 2.0

【重要】EF开发技巧

  1. 当EF编写复杂查询时,EF和Linq不是很好的方案,使用纯Sql效率会更高;
  2. 推荐多种数据库访问工具一起开发,如直接用Sql的查询可以使用更轻量级的Dapper实现;
  3. EF的延时加载是把双刃剑,在很多情况下,采用即时加载(Include)能极大的提高数据操作效率,如批量导入,可以采用及时加载的形式;
  4. EF另一个容易引发性能问题的点是修改跟踪对象的状态,所以EF对象集合不应该当成普通的对象集合,如果是批量添加数据先加到一个普通集合在通过AddRange加到EF对象集合上;
  5. 大佬连接: Arthur Blog
  6. 大佬链接:Jeffcky
  7. 微软官方文档:https://docs.microsoft.com/zh-cn/ef/ef6/querying/local-data
  8. 实际上一直自己开关状态监测,对开发人员是一件非常不爽的事情,也失去一部分使用EF的乐趣,一般简单的处理方式是,查询和向DTO或UI输出的实体,直接加上AsNoTracking(),如果需要业务处理,在NoTracking状态下,做好实体的各种变更,再Attach()回来,手动修改一下EntryState会避免很多不必要的麻烦;

问题列表

  1. .Net Standard与.Net Framework的区别于联系;
  2. EF Core比EF性能更高?
  3. EF中dbContext.GetValidationErrors()的作用;
  4. modelBuilder.ComplexType<Address>();干什么的?
  5. EF中的对context.Entry<T>().Load()与context.Entry<T>().ToList()的区别?
  6. EF实体查询中,SingleOrDefault()与FirstOrDefault()还有Find的区别?
  7. 数据库中的表值函数是什么(who),有什么用(what),怎么用(how)?
  8. 5W2H分析法

笔记

  1. Context中如果只读可以写为:
    public DbSet<Blog> Blogs{ get {return Set<Bolg>();} }
  2. EF初始化数据库策略有三种:不存在创建、总是创建新数据库、如果模型发生改变更新数据库模型;可以通过Database.SetInitializer(...) //如果传null怎不进行执行数据库初始化策略,也可以通过App.config进行配置
  3. 表主键如果不指定,默认只能是类名称+ID或ID,不然EF会提示未设置主键;
  4. 除了定义导航属性外,建议在依赖对象上定义外键属性:
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;}
}
  1. 复杂类型的实体(如实体中存在内部类),建议与实体类映射在一个表中:
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
...
  1. 自定义约定
//将所有实体中类型为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));
  1. 自定义配置
//自定义配置
public class MySqlConfiguration : DbConfiguration
{
	public MySqlConfiguration()
	{
		//SetProviderServices(...);
	}
}
----------------------------------------------------------------------
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlConfiguration))]
public class MysqlContext : SqlServerContext
...
  1. 特殊的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();
  1. Context中用到的方法
//获取实体EntityA中复杂类型ComplexObj的ComplexPropertyA属性的原始值
var ComplexPropertyA = context.Entry(EntityA).ComplexProperty(e => e.ComplexObj).Property(c => c.ComplexPropertyA).OriginalValue;

  1. 实体配置类,需要继承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中
	}
}
  1. 映射的三种模式TPH、TPT、TPC
    TPH:Table per Hierarchy,将多个类放到同一个表中,用discriminator区分类型,继承常用
    THT:Table per Type,用外键表示继承关系
    modelBuilder.Entity<T>().Table("T");
    
    THC:Table per Concreate class,最不被推荐的,继承关系中,每个子表会包含所有父类的字段,相当于很多数据表数据字段是重复的
    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
  1. 将数据表合并为一个实体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
  1. Cast与OfType都可以转换类型,但是OfType是在数据库中查询,Cast都查询出来进行转换;
  2. 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();
  3. 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;
  4. EF sql增删改:context.Database.ExecuteSqlCommand(sql, parameters);
  5. 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
    {
    	//更新失败
    }
    
    
    
  6. EF批量更新,可以使用EntityFramework.Extended插件来实现
context.Entities.Where(e => e.Name == "abc").Update(e => new Entity(){Property1 = "updateProperty1"});
context.SaveChanges();
  1. EF 自动创建存储过程,如果不想通过Add或Remove来操纵实体,可以使用自动生成的存储过程操作实体
public class EntityMap : EntityTypeConfiguration<Entity>
{
	public EntityMap()
	{
		//在迁移中会自动生成增删改的三个存储过程
		MapToStoredProcedures();
	}
}
--------------------------------------------------------------
//使用
context.Database.ExecuteSqlCommand("dbo.Entity_update @Name", parameters);
  1. EF中使用Configuration.UseDatabaseNullSemantics=true;来生成更简单的查询语句;
  2. 表值函数在EF中的调用
context.Database.SqlQuery<Entity>("Select * From [dbo].[GetEntities]()");
  1. EF中对日期操作
//不能直接用DateTime.Now - e.ModifyTime
context.Entities.Select(e => new { Time = SqlFunctions.DateDiff("DAY", e.ModifyTime, DateTime.Now)}).ToList();
  1. 使用AutoFac、NinjectMvc管理Context的生命周期
  2. 快照是变更跟踪,使用Configuration.AutoDetectChangesEnabled= true来开启,开启后在修改属性时会调用DetectChanges,而context.Entry会在开启AutoDetectChangesEnabled时调用DetectChanges,快照式变更利用了POCO的快照相关行为;
  3. 代理式变更追踪,需要加virtual,使用Configuration.ProxyCreationEnabled = true来开启,生成EF的代理类来通知virtual的属性变更,所以在AutoDetectChangesEnabled = false; 加virtual的属性变更也能被发现;
var obj = ((IObjectContextAdapter)context).ObjectContext;
//创建Entity的代理类,entity的类型为Entity在EF中的代理类
var entity = obj.CreateObject<Entity>();
  1. 延迟加载必须满足三个条件
    Configuration.ProxyCreationEnabled = true;
    Configuration.LazyLoadingEnabled = true;
    导航属性必须为virtual;

  2. 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进行更新

  3. 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对字符串首尾空格处理不一致的问题
  1. 事务

事务默认不在查询中使用;
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右键进行配置。

  1. 数据库弹性连接(重试机制,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));
    }
}
  1. 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

  1. app.config中设置连接字符串,增加"MultipleActiveResultSets=True;"可以是实现单连接多请求。
  2. 解决EF性能差的问题:https://blog.csdn.net/csdnnews/article/details/100111387
  3. 使用AsNoTracking()可以解决查询多,效率低的问题。
  4. 实体缓存
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);
}
  1. 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异常
  1. 翻译缓存

·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

  1. 重新编译查询

可以利用拦截器实现拦截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进行重新编译查询

拦截器实现:https://gitee.com/EverestVIP/EntityFramework6.x-And-Core2.0/blob/master/EntityFramework6/EntityFramework6/EntityFramework6/EFInterceptor/RecompileDbCommandInterceptor.cs

  1. EF N + 1查询优化
    如果使用POCO对象时,关联集合对象没有Include加载,在使用关联集合时,每次都会到数据库查询关联集合数据;
    解决办法:
    ①查询POCO对象时,所有用到的延时加载数据都要Include加载;
    ②使用DTO 与 POCO进行映射,映射工具推荐使用AutoMapper。
  2. EF添加索引可以提高效率
  3. 批量插入可以使用SQLBulkCopy提高性能,只适用于SQLserver
    如果不使用SqlBulkCopy,可以使用AddRange + (AutoDetectChangesEnabled = false)进行效率提升,AutoDetectChangesEnabled 可以放到try/finally块里操作;
  4. 并发情况可以使用异步操作:XXXAsync()方法
  5. EF实战搭建可以使用:工作单元泛型仓储模式依赖注入泛型仓储模式
  6. EF6迁移遵循规则 【见详细迁移

对于EF的迁移我们要记住以下三点
①Entity Framework 实体框架使用_MigrationHistory表来追踪应用于数据库的更改
②Entity Framework 实体框架创建项目中当前模型状态的哈希值,并将其与_MigrationHistroy表中的模型状态存储进行比较,以确定数据库是否为当前状态
③进行迁移时,始终按照时间戳(升序)的顺序同步到数据库

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值