介绍
大家都清楚,EF Core 2在8月14日发布了。它加入了一些新的东西,也对之前的内容进行了一些变化。遗憾的是,它仍然没有包含 pre-Core版本中的一些特性,而且需求很高,比如对延迟加载和Group by的支持。
我们来看看这个列表
.NET Standard 2.0
刚刚发布的Entity Framework Core 2 现在是基于.NET Standard 2.0的。这意味着它可以在很多环境中使用,只要是支持它的平台上都有用。
改进SQL生成
改进包括:
不需要嵌套子查询的时候,不会进行嵌套子查询的创建
只对需要查询的列进行映射
不会在一个LINQ查询中创建多个SQL查询了
自有实体(Owned Entities)
复杂类型回归,现在它们被称为自有实体了。请记住,复杂类型和实体之间的区别在于前者没有标识。例如,一个Address类和很多属性,Personal,Work等等;所有这些属性都可以映射到这个类,它们将被存储在与包含实体相同的表中。看起来像这个:
modelBuilder.Entity<Customer>().OwnsOne(c=>c.PersonalAddress);
当然,你也可以把这些属性的上下文放到其他的表里面,你只需要这样做:
modelBuilder.Entity<Customer>().OwnsOne(c=>c.PersonalAddress).ToTable(“CustomerAddress”);
当然,你可以一次性拥有很多属性,不过这个时候,你就无法通过属性来声明自有实体。
分表(Table Splitting)
你可以拥有多个不同的类同时指向一张表,Entity Framework Core允许你这样做。这些类可以暴露出不同的属性。
实体状态监听(Entity State Listener)
这是一个默认就被注册的新街口,ILocalViewListener,他可能用于跟踪实体的变化,但是可惜不是实例化后的实体。
varevents=ctx.GetService<ILocalViewListener>();events.RegisterView((entry,state)=>{//entrycontainstheentityandstateitscurrentstate});
在这里你需要注意,你不能够使用这个来取消改动,因为它只会在实际事件发生后才进行调用。
多元化(Pluralization)
来说一下这个新的接口 IPluralizer和它的空实现NullPluralizer。当EF生成数据库(EF更新数据库)或者生成实体并根据其生成类时(Scaffold-DbContext),它可以用于复数的表名。使用它是有难度的,我们需要有一个类实现IDesignTimeServices接口,这个类通过下面的工具自动检索:
publicclassCustomPluralizerDesignTimeServices:IDesignTimeServices{publicvoidConfigureDesignTimeServices(IServiceCollectionservices){services.AddSingleton<IPluralizer,CustomPluralizer>();}}publicclassCustomPluralizer:IPluralizer{publicstringPluralize(stringname){return...;}publicstringSingularize(stringname){return...;}}
因为工具还依赖于依赖注入框架,我们通过IPluralizer 接口进行另一种实现。
数据库上下文池(DbContext Pools)
通常当DbContext被依赖注入框架在什么地方注入后,每次将会创建一个新的实例。这样,我们可以建立一个实例的资源池,默认一个资源池可以有128个实例。这样将会提升效率,具体配置如下:
services.AddDbContextPool<DataContext>(options=>{//...},poolSize:256);
Attach
连接现有实体的连接方法现在更聪明了:如果所连接图形中的任何一个实体都有它的键集,它将被标记为unchanged,如果不是,则是new。
Entity Type Configuration
现在我们可以将实体配置存储在不同的类中,就和我们以前拥有的类一样:
publicclassMyEntityTypeConfiguration:IEntityTypeConfiguration<MyEntity>{publicvoidConfigure(EntityTypeBuilder<MyEntity>builder){//...}}
只是这些类不会被自动的发现
modelBuilder.ApplyConfiguration(newMyEntityTypeConfiguration());
全局过滤器(Global Filters)
全局过滤器在pre-Core EF中就已经存在了,他们在通常使用在以下两种情形:
对于多租户应用程序
软删除
软删除是这样来配置的:
modelBuilder.Entity<Post>().HasQueryFilter(p=>!p.IsDeleted);
多租户是这样的:
modelBuilder.Entity<Blog>().HasQueryFilter(p=>p.TenantId==this.TenantId);
它将对任何作为查询结果(包括渴望负载)或从一到多个集合加载的实体应用过滤,但它不会通过id(一对一或多对一)过滤查询。
您可以显式调用IgnoreQueryFilters 扩展方法禁用任何现有的过滤器:
ctx.Blog.IgnoreQueryFilters().ToList();
禁用客户端赋值(Disabling Client-Side Evaluation)
您可能知道EF Core可以对客户端不知道的方法进行客户端赋值,也就是说,不能将其转换为数据库调用。这是透明的,可能会变成性能问题。如果需要禁用此功能,现在可以通过配置日志基础结构在客户端赋值发生时抛出异常:
varbuilder=newDbContextOptionsBuilder<DataContext>().ConfigureWarnings(options=>{options.Throw(RelationalEventId.QueryClientEvaluationWarning);options.Default(WarningBehavior.Log);});
Like
现在我们支持SQL的like函数了,虽然在过去我们也支持类似的东西,通过String.Contains的方法。
就好像这样:
ctx.Posts.Where(x=>EF.Functions.Like(x.Title,“%NET%”).ToList();
不幸的是,微软没有把Like做成一个扩展方法,我认为它更好用。
调用标量函数(Calling Scalar Functions)
调用标量函数也回来了,有一些小的注意事项:
这些功能需要在类中声明静态语境
他们只能返回标量值作为参数
举个例子,首先声明,使用T-SQL的内置函数,通过[ DbFunction]属性:
[DbFunction]publicstaticstringSoundex(stringname){thrownewNotImplementedException();}
或者
modelBuilder.HasDbFunction(this.GetType().GetMethod(“Soundex”));
无论哪种情况,您都可以指定模式或函数的名称,如果它与所使用的方法不同:
[DbFunction("FuncName",Schema="dbo")]modelBuilder.HasDbFunction(this.GetType().GetMethod("FuncName"),options=>{options.HasName("FuncName");options.HasSchema("dbo");});
或者这样用
varsounds=ctx.MyEntity.Select(x=>x.Soundex(x.Name)).ToList();
支持字符串插值(String Interpolation Support)
现在,FromSql和ExecuteSqlCommand方法支持插入字符串了,并且它会产生所需的参数。您不必担心那些由于查询计划创建时而引起的SQL注入攻击和性能问题了。
具体是这样做的:
ctx.Database.ExecuteSqlCommand($"UPDATERecordSETValue={value}WHEREId={id}");
显式编译查询(Explicitly Compiled Queries)
Entity Framework Core包含了version 1以来就有的查询缓存,但仍有一些开销是与从查询中计算密钥并从缓存中获取密钥有关。因此,version 2引入了Entity Framework pre-Core 中就存在的LINQ to SQL能力:显式查询编译和执行。通过这一点,我们能够预先编译一个查询,并在我们想要的任何上下文中使用它(当然是兼容类型的)。我们甚至可以热切取相关的集合或实体:
staticreadonlyFunc<MyEntityContext,int,IEnumerable<MyEntity>>CompiledQuery=EF.CompileQuery<MyEntityContext,int,MyEntity>((ctx,id)=>ctx.MyEntities.Where(x=>x.Id==id).Include(x=>x.Others).OrderBy(x=>x.Name));
如您所见,它返回一个委托,我们可以调用它传递适当的参数——在这个例子中,有一个上下文和一个参数,但是我们可以提到到有8个不同类型的参数:
varresults=CompiledQuery(ctx,100).ToList();
打断变化(Breaking Changes)
IDbContextFacfory<T>接口被IDesignTimeDbContextFactory<T>接口所替代,CreateDbContext这个签名方法当然也被修改了:
publicclassDummyContextFactory:IDesignTimeDbContextFactory<DummyContext>{publicDummyContextCreateDbContext(string[]args){varbuilder=newDbContextOptionsBuilder<DummyContext>();builder.UseSqlServer("…");returnnewDummyContext(builder.Options);}}
UseInMemoryDatabase现在需要一个名字了:
optionsBuilder.UseInMemoryDatabase("MyDatabase")
Microsoft.EntityFrameworkCore.SqlServer.Design过时了,需要改成使用Microsoft.EntityFrameworkCore.Design。
只有2.0 内容的能够工作, 因此,基于EF Core 1.x 的内容基本都需要重写。
本文暂时没有评论,来添加一个吧(●'◡'●)