当前位置: 首页 > news >正文

视频网站视频预览怎么做的做u盘的老外网站

视频网站视频预览怎么做的,做u盘的老外网站,网站可以嵌入WordPress,茶叶网站源码 下载本文介绍了一个在EntityFramework Core 5中不需要预先加载数据而使用一句SQL语句批量更新、删除数据的开发包#xff0c;并且分析了其实现原理#xff0c;并且与其他实现方案做了比较。一、背景随着微软全面拥抱开源#xff0c;.Net开源社区百花开放#xff0c;涌现了非常多… 本文介绍了一个在EntityFramework Core 5中不需要预先加载数据而使用一句SQL语句批量更新、删除数据的开发包并且分析了其实现原理并且与其他实现方案做了比较。一、背景随着微软全面拥抱开源.Net开源社区百花开放涌现了非常多优秀的开源ORM项目就有Dapper、SqlSugar、PetaPoco、FreeSQL等。作为微软官方提供的ORM框架Entity Framework Core以下简称EF Core显然是被关注最多的。EF Core非常优秀而且功能丰富但是EF Core有一个一直被人诟病的地方就是它并不能很好支持数据的批量更新和批量删除。在EF Core中批量更新和删除数据都要先把数据加载到内存中然后再对数据操作最后再SaveChanges比如下面的代码用于把所有Id大于2或者AuthorName中含有”zack”的价格增加3var books2 ctx.Books.Where(b b.Id 2||b.AuthorName.Contains(zack));foreach(var b in books2){    b.Price b.Price 3;}ctx.SaveChanges(); 让我们查看上面的程序幕后执行的SQL语句 可以看到EF Core先把数据用Select查询出来然后在内存中逐个修改最后再把被修改对象每个都执行一次Update语句去更新。 再比如如下的代码用于删除Price大于5元的记录var books1 ctx.Books.Where(b b.Price 5);ctx.RemoveRange(books1);ctx.SaveChanges(); 让我们查看上面的程序运行幕后执行的SQL语句  可以看到EF Core先把数据用Select查询出来然后再对每条记录都执行Delete语句去删除。很显然如果批量更新或者删除的数据量比较大这样的操作性能是非常低的。因此我们需要一种在EF Core中使用一条SQL语句就高性能地删除或者更新数据的方法。 二、为什么微软不提供这样的方法尽管用户的要求强烈但是微软一直没有提供高效的批量删除和更新的方式。在EF Core Github的issue中 [1]微软给出的理由是这样做会导致EF Core的对象状态跟踪混乱比如对于同一个DbContext如果用批量删除的方法删除了数据那么在被删除之前查询出来的数据状态就混乱了因此需要重构EF Core的代码工作量比较大。作为一个成熟的框架考虑这些逻辑问题以避免潜在的风险是有必要的是可以理解的。但是作为实际的开发者我们是有办法规避这些问题的。比如一般的Web应用中删除操作都是在一个单独的Http请求进行中的因此不涉及到微软担心的问题。即使在有的场景下涉及到在通过同一个DbContext在数据删除之前就把数据查询出来的场景那么也完全可以通过在删除之后再查一次的方式来规避这个问题。根据github上那个issue的回复微软有考虑在EF Core 6.0中加入高效地批量删除和更新数据的方式但是仅仅是“考虑”并不确定。我们作为普通开发者可等不及了因此要自己去解决。三、已有解决方法有如下三种已有的解决方法执行原生SQL语句。在EF Core中提供了ctx.Database.ExecuteSqlRaw()等方法可以用来执行原生SQL语句因此我们可以直接编写Delete、Update语句来删除或者更新数据。这种方式比较直接缺点就是这样代码中直接操作数据表的方式不太符合模型驱动、分层隔离等思想程序员直接面对数据库表无法利用EF Core强类型的特性如果模型发生改变必须手动变更SQL语句而且如果调用了一些DBMS特有的语法、函数一旦程序迁移到其他DBMS就可能要重新编写SQL语句而无法利用EF Core强大的SQL翻译机制来屏蔽不同底层数据库的差异。使用其他ORM。FreeSQL等ORM中提供了批量Delete、Update语句的方法使用也非常简单。这种方式的缺点是项目中必须引入第三方的ORM无法复用EF Core的代码。使用已有的EF Core扩展。EF Plus、EFCore.BulkExtensions等开源库中都提供了在EF Core框架下进行批量操作的方法。实现这个的核心就是要获得EF Core生成的SQL语句以及SelectExpression。由于EF Core 5.0之前的版本中没有提供公开的API用于获取一个LINQ操作对应的SQL语句所以这些开源库都是通过访问EF Core框架中一些类的私有成员来完成的获取LINQ对应的SQL语句以及SelectExpression的方法 [2]。由于用的是访问私有成员这样不符合面向对象原则的方式所以一旦EF Core框架代码发生改变代码就可能会失败之前就发生过EF Core新版本发布造成这些开源库无法工作的情况。而且在撰写这篇文章的时候这些开源库还没有适配.Net 5。 四、我的实现Zack.EFCore.Batch我开发了一个EntityFramework Core的扩展库让开发者在Entity Framework Core中可以用一句SQL进行数据的删除或者更新。由于开发中用到了Entity Framework Core5的API所以这个库要求Entity FrameworkCore 5及以上版本也就是.Net 5及以上版本。 下面介绍一下使用方法第一步通过Nuget安装Install-Package Zack.EFCore.Batch第二步把如下代码添加到你的DbContext类的OnConfiguring方法中optionsBuilder.UseBatchEF();第三步: 使用DbContext的扩展方法DeleteRangeAsync()来删除一批数据. DeleteRangeAsync()的参数就是过滤条件的lambda表达式。批量删除的例子代码如下: await ctx.DeleteRangeAsyncBook(b b.Price n || b.AuthorName zack yang); 上面的代码将会在数据库中执行如下SQL语句Delete FROM [T_Books] WHERE ([Price] __p_0) OR([AuthorName] __s_1) DeleteRange()方法是DeleteRangeAsync()的同步方法版本。使用DbContext的扩展方法BatchUpdate()来创建一个BatchUpdateBuilder对象。BatchUpdateBuilder类有如下四个方法Set()方法用于给一个属性赋值。方法的第一个参数是属性的lambda表达式,第二个参数是值的lambda表达式。Where() 是过滤条件ExecuteAsync()使用用于执行BatchUpdateBuilder的异步方法Execute()是ExecuteAsync()的同步方法版本。 例子代码:await ctx.BatchUpdateBook()   .Set(b b.Price, b b.Price 3)   .Set(b b.Title, b s)  .Set(bb.AuthorName,bb.Title.Substring(3,2)b.AuthorName.ToUpper())   .Set(b b.PubTime, b DateTime.Now)   .Where(b b.Id n || b.AuthorName.StartsWith(Zack))  .ExecuteAsync(); 上面的代码将会在SQLServer数据库中执行如下SQL语句Update [T_Books] SET [Price] [Price] 3.0E0,[Title] __s_1, [AuthorName] COALESCE(SUBSTRING([Title], 3 1, 2), N) COALESCE(UPPER([AuthorName]), N), [PubTime] GETDATE()WHERE ([Id] __p_0) OR ([AuthorName] IS NOT NULLAND ([AuthorName] LIKE NZack%)) 这个开发包使用EFCore实现的lambda表达式到SQL语句的翻译所以几乎所有EF Core支持的lambda表达式写法都被支持。 项目的GitHub地址https://github.com/yangzhongke/Zack.EFCore.Batch五、实现原理分析其实要把lambda表达式转换为SQL语句并不难只要对表达式树进行解析就可以生成SQL语句但是最难的部分是对于.Net函数到SQL片段的翻译因为相同的.Net函数在不同DBMS中等效的SQL片段是不同的如果我自己实现这个是很麻烦的因此我想到了直接借用EF Core的表达式树到SQL语句的翻译引擎来实现是最佳的方法。不幸的是在.NetCore 3.x及之前是无法直接获取一个Linq查询翻译后的SQL语句的。.Net Core中可以通过日志等方式获取翻译后的SQL语句但是这些都是Linq执行后才能获得的而且是无法在拿到一个Lambda表达式或者IQueryable的时候立即获得SQL的。经过询问.Net Core开发团队得知在.Net Core 3.X及之前也是没有公开的API可以完成表达式树到SQL片段翻译的功能。 从.Net 5开始Entity Framework Core 中提供了不用执行查询就可以直接获取Linq查询对应的SQL语句的方法那就是调用IQueryable的ToQueryString()方法 [3]。 因此我就想通过这个ToQueryString()方法拿到的SQL语句来入手来实现这个功能。可以把用到的Lambda表达式片段、过滤表达式拼接到一个查询表达式中然后调用ToQueryString()方法获取翻译后的SQL语句然后编写词法分析器和语法分析器对SQL语句进行分析提取出Where子句以及Select列中的表达式片段然后再把这些片段重新组合成Update、Delete的SQL语句即可。不过由于不同DBMS的语法不同编写这样的词法及语法分析器是很麻烦的我就想能否研究ToQueryString()的实现原理然后直接拿到解析过程中的SQL片段这样就避免了生成SQL后再去解析的工作。虽然EF Core是开源的不过由于关于EF Core的源代码并没有一个全面介绍的文档而EF Core的代码又是非常复杂的所以研究EF Core的源代码是非常耗时的。研究过程中我几次都想要放弃最后终于把功能实现了通过开发这个库我也对于EF Core的内部原理特别是从Lambda表达式到SQL的翻译的整个过程了解的非常透彻。我这里不对研究的过程去回顾而是直接为大家讲解一下EFCore的原理然后再讲解一下我这个Zack.EFCore.Batch的实现原理。1.  EF Core的SQL翻译原理EF Core中有很多的服务比如对于IQueryable进行预处理的QueryTranslationPreprocessor、从查询中提取查询参数的RelationalParameterBasedSqlProcessor、把表达式树翻译为SQL语句的QuerySqlGenerator等。这些服务一般都是通过IXXX Factory这样的工厂类的Create()方法创建的比如QueryTranslationPreprocessor对应的IQueryTranslationPreprocessorFactory、QuerySqlGenerator对应的IQuerySqlGeneratorFactory。而这些工厂类的对象则是通过dbContext.GetServiceXXX()来从DbContext中获得的。当然也有的服务是不需要通过工厂直接获得的比如Lambda编译器服务IQueryCompiler就可以直接通过ctx.GetServiceIQueryCompiler()获取。 因此如果你想使用EF Core中其他的服务都可以尝试把对应的服务接口类型或者工厂类型放到GetService()中查询一下试试。EF Core中还允许调用DbContextOptionsBuilder的ReplaceService()方法把EF Core中的默认服务替换为自定义实现类。 EF Core中把一个IQueryable对象翻译为SQL语句的代码分散在各个类中我经过努力把它们整合为一段可以运行的代码如下 Expression query queryable.Expression;var databaseDependencies ctx.GetServiceDatabaseDependencies();IQueryTranslationPreprocessorFactory_queryTranslationPreprocessorFactory ctx.GetServiceIQueryTranslationPreprocessorFactory();IQueryableMethodTranslatingExpressionVisitorFactory_queryableMethodTranslatingExpressionVisitorFactory ctx.GetServiceIQueryableMethodTranslatingExpressionVisitorFactory();IQueryTranslationPostprocessorFactory_queryTranslationPostprocessorFactory ctx.GetServiceIQueryTranslationPostprocessorFactory();QueryCompilationContext queryCompilationContext databaseDependencies.QueryCompilationContextFactory.Create(true); IDiagnosticsLoggerDbLoggerCategory.Querylogger ctx.GetServiceIDiagnosticsLoggerDbLoggerCategory.Query();QueryContext queryContext ctx.GetServiceIQueryContextFactory().Create();QueryCompiler queryComipler ctx.GetServiceIQueryCompiler() as QueryCompiler;//parameterize determines if it will use Declareor notMethodCallExpression methodCallExpr1 queryComipler.ExtractParameters(query, queryContext, logger, parameterize:true) as MethodCallExpression;QueryTranslationPreprocessorqueryTranslationPreprocessor _queryTranslationPreprocessorFactory.Create(queryCompilationContext);MethodCallExpression methodCallExpr2 queryTranslationPreprocessor.Process(methodCallExpr1) as MethodCallExpression;QueryableMethodTranslatingExpressionVisitorqueryableMethodTranslatingExpressionVisitor        _queryableMethodTranslatingExpressionVisitorFactory.Create(queryCompilationContext);ShapedQueryExpression shapedQueryExpression1 queryableMethodTranslatingExpressionVisitor.Visit(methodCallExpr2) asShapedQueryExpression;QueryTranslationPostprocessor queryTranslationPostprocessor_queryTranslationPostprocessorFactory.Create(queryCompilationContext);ShapedQueryExpression shapedQueryExpression2 queryTranslationPostprocessor.Process(shapedQueryExpression1) asShapedQueryExpression; IRelationalParameterBasedSqlProcessorFactory_relationalParameterBasedSqlProcessorFactory        ctx.GetServiceIRelationalParameterBasedSqlProcessorFactory();RelationalParameterBasedSqlProcessor_relationalParameterBasedSqlProcessor _relationalParameterBasedSqlProcessorFactory.Create(true); SelectExpression selectExpression (SelectExpression)shapedQueryExpression2.QueryExpression;selectExpression _relationalParameterBasedSqlProcessor.Optimize(selectExpression,queryContext.ParameterValues, out bool canCache);IQuerySqlGeneratorFactory querySqlGeneratorFactory ctx.GetServiceIQuerySqlGeneratorFactory();QuerySqlGenerator querySqlGenerator querySqlGeneratorFactory.Create();var cmd querySqlGenerator.GetCommand(selectExpression);string sql cmd.CommandText; 大致解释一下上面的代码queryable是一个待转换的IQueryable对象ctx是一个DbContext对象。QueryCompilationContext是Lambda到SQL翻译这个“编译”过程的上下文很多工厂类的Create方法都要用它做参数。QueryContext是查询语句的上下文。SelectExpression是Linq查询的表达式树翻译为强类型的抽象语法树的树根。QuerySqlGenerator的GetCommand()方法用于遍历SelectExpression生成目标SQL语句。QuerySqlGenerator的GetCommand方法最终会调用VisitSelect(SelectExpressionselectExpression)来拼接生成SQL语句其中会调用VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)、VisitFromSql(FromSqlExpression fromSqlExpression)、VisitLike(LikeExpression likeExpression)等方法来把运算表达式、From、Like等翻译成对应的SQL片段。由于不同DBMS中一些函数等实现不同而SelectExpression、LikeExpression等都是一个抽象节点是独立于具体DBMS的抽象模型因此各个DBMS的EF Provider只要负责编写代码把这些XXExpression翻译为各自的SQL片段即可不同DBMS的EF Core中的代码大部分都是各种XXTranslatorProvider。2.  Zack.EFCore.Batch的实现原理这个库最核心的代码就是ZackQuerySqlGenerator它是一个继承自QuerySqlGenerator的类。它通过override父类的VisitSelect方法然后把父类的VisitSelect方法的代码全部拷过来。这样的目的就是在VisitSelect拼接SQL语句的过程中把各个SQL片段截获到。以下面的代码为例if (selectExpression.Predicate ! null){       Sql.AppendLine().Append(WHERE);       varoldSQL Sql.Build().CommandText;//zacks code       Visit(selectExpression.Predicate);       this.PredicateSQL Diff(oldSQL, this.Sql.Build().CommandText); //zacks code}这里就是首先把拼接Where条件之前的SQL语句保存到oldSQL变量中再把拼接Where条件之后的SQL语句和oldSQL求一个差运算就得到了Where语句的SQL片段。 然后通过optBuilder.ReplaceServiceIQuerySqlGeneratorFactory,ZackQuerySqlGeneratorFactory();把ZackQuerySqlGenerator对应的ZackQuerySqlGeneratorFactory替换为IQuerySqlGeneratorFactory的默认实现。这样EF Core再完成从SelectExpression到SQL语句的翻译就会使用ZackQuerySqlGenerator类这样我们就可以截获翻译生成的SQL片段了。 再解释一下批量更新数据库的BatchUpdateBuilder类的主要代码。代码主要就是把AgeAge1,NameAuthorName.Trim()这样的赋值表达式重新生成Select(new{b.Age,b.Age1,b.Name,b.AuthorName.Trime()})这样的表达式这样就把N个赋值表达式重新拼接为2*N个查询表达式再把查询条件拼接形成一个IQueryable对象再调用ZackQuerySqlGenerator翻译IQueryable获取到Where的SQL片段以及各个列的SQL片段最后重新拼接成一个Update的SQL语句。 六、局限性Zack.EFCore.Batch有如下局限性由于Zack.EFCore.Batch用到了EF Core 5.0的新API所以暂不支持EF Core 3.X及以下版本。由于Zack.EFCore.Batch是直接操作数据库所以更新、删除后会存在微软担心的同一个DbContext中已经查询出来的对象跟踪状态和数据库不一致的情况。在同一个DbContext实例中如果需要在批量删除或者更新之后操作同一个DbContex中之前查询出来的数据建议再执行一遍查询操作。代码中使用了一个内部API QueryCompiler这是不推荐的做法。
http://www.zqtcl.cn/news/616258/

相关文章:

  • 定西模板型网站建设网络架构和现实架构的差异
  • 做搜索的网站做网站的代码有哪些
  • 视频制作网站推荐js做音乐网站
  • 海北wap网站建设公司有后台网站怎么做
  • 织梦网站最新漏洞入侵外贸网站模板有什么用
  • 在跨境网站贸易公司做怎么样网站建设维护合同范本
  • 网站必须做可信认证南山网站制作
  • 如何使用mysql数据库做网站企业管理专业大学排名
  • 九江网站建设九江深圳网站建设费用大概多少
  • 万网站长工具郑州seo哪家公司最强
  • 宁波哪里可以做网站企业网站源码哪个好
  • 网站每天点击量多少好精选聊城做网站的公司
  • 网站建设课程基础兰州网站seo费用
  • 天助可以搜索别人网站曲靖网站推广
  • 易语言编程可以做网站么网站备案流程
  • 我想接加工单seo搜索引擎优化工资
  • 西宁做网站君博推荐wordpress如何管理
  • 个人建一个网站多少钱怎样优化网络速度
  • 网站建设项目进度表长春百度seo代理
  • 购物网站排名哪家好免费做房产网站
  • 手机免费建设网站制作南通网站建设排名公司哪家好
  • 做商城网站哪里买企业官网招聘
  • 网站自己做流量互联网营销培训平台
  • 如何查看网站备案官方网站建设状况
  • 做什麽网站有前景软件 开发 公司
  • 淘宝做短视频网站好建设银行代发工资网站
  • 北京建商城网站网站做指向是什么意思
  • 定制网站开发介绍图移动网站适配
  • 青海网站建设怎么建设腾云建站官网
  • 怎样自己做企业的网站gif制作软件app