前言

上一节我们讲完原始查询如何防止SQL注入问题同时并提供了几种方式。本节我们继续来讲讲EF Core 2.0中的新特性自定义标量函数。

自定义标量函数两种方式

在EF Core 2.0中我们可以将方法映射到数据库中的标量函数,我们可在LINQ中调用此方法并会被正确翻译成SQL语句,这为编写数据访问层的开发人员提供了一个很棒的功能来创建一个方法并在其上应用DbFunction特性即可。该属性会将静态CLR方法映射到数据库函数,以便可以在LINQ查询中使用此方法。默认情况下,数据库函数中的CLR静态方法名称必须相同,除非我们在DbFunctionAttribute中指定了不同的名称。自定义标量函数必须满足如下两个条件。

(1)函数必须是静态方法且在上下文中声明。

(2)只能作为参数标量值返回。

自定义标量函数方式一

我们可直接在上下文中定义一个静态方法,如下:

  1. [DbFunction]
  2. public static string ScalarFunction(string name)
  3. {
  4. throw new NotImplementedException();
  5. }

自定义标量函数方式二

  1. public static string ScalarFunction(string name)
  2. {
  3. throw new NotImplementedException();
  4. }

然后在OnModelCreating方法利用ModelBuilder中的HasDbFunction来调用上述方法,如下:

  1. modelBuilder.HasDbFunction(
  2. () => ScalarFunction(null));

请注意以上自定义标量函数的两种方式必须定义架构名称即Schema,否则在调用上述方法查询时将抛出【System.Data.SqlClient.SqlException:“'您自定义的函数名称' 不是可以识别的 内置函数名称。”】,也就是说我们无论是利用DbFunction特性还是HasDbFunction方法映射自定义标量函数也好都必须指定Schema,我们默认指定为dbo,如下:

  1. [DbFunction(FunctionName = "UdfFunction", Schema = "dbo")]
  2. public static string ScalarFunction(string name)
  3. {
  4. throw new NotImplementedException();
  5. }

或者

  1. public static string ScalarFunction(string name)
  2. {
  3. throw new NotImplementedException();
  4. }
  5.  
  6. modelBuilder.HasDbFunction(
  7. () => ScalarFunction(null)).HasName("UdfFunction").HasSchema("dbo");
  8. 或者
  9. modelBuilder.HasDbFunction(GetType()
  10. .GetMethod("ScalarFunction"), options =>
  11. {
  12. options.HasName("UdfFunction");
  13. options.HasSchema("dbo");
  14. });

上述讲解了在EF Core 2.0中如何创建标量函数,讲了这么多,到底怎么用,或者说它的出现可以解决什么问题呢?下面我们首先来看一个例子。比如我们想查询每篇博客的评论数的均值,接下来我们会进行如下查询:

  1. using (var context = new EFCoreDbContext())
  2. {
  3. var blogs = context.Blogs
  4. .AsNoTracking();
  5.  
  6. var result = blogs.Select(b => new BlogDTO()
  7. {
  8. Id = b.Id,
  9. Name = b.Name,
  10. Count = b.Posts.Count() > ? b.Posts.Average(d => d.CommentCount) :
  11. }).ToList();
  12. }

此时将出现函数Average无法翻译成SQL,只能在内存中进行查询。在EF Core中如果您有详细查看过生成的SQL语句的话,您就能够明白,对于Min、Max、Average等LINQ函数,EF Core不支持翻译成远程SQL,只能在本地查询。此时我们再来看看进行此次查询总共耗时100ms,如下:

接下来我们再利用自定义标量函数查询试试。首先定义标量函数

  1. public static double? UdfAverage(int blogId)
  2. {
  3. throw new Exception();
  4. }
  1. modelBuilder.HasDbFunction(
  2. () => UdfAverage(default(int))).HasSchema("dbo");

然后我们再来创建标量函数

  1. public static class AddUdfHelper
  2. {
  3. public static void AddUdfToDatabase(this DbContext context)
  4. {
  5. using (var transaction = context.Database.BeginTransaction())
  6. {
  7. try
  8. {
  9. context.Database.ExecuteSqlCommand(
  10. "IF OBJECT_ID('dbo.UdfAverage', N'FN') IS NOT NULL " +
  11. "DROP FUNCTION dbo.UdfAverage");
  12.  
  13. context.Database.ExecuteSqlCommand(
  14. "CREATE FUNCTION UdfAverage (@blogId int)" +
  15. @" RETURNS FLOAT
  16. AS
  17. BEGIN
  18. DECLARE @result AS FLOAT
  19. SELECT @result = AVG(CAST([CommentCount] AS FLOAT)) FROM dbo.Posts AS p
  20. WHERE p.BlogId = @blogId
  21. RETURN @result
  22. END");
  23. transaction.Commit();
  24. }
  25. catch (Exception ex)
  26. {
  27. throw ex;
  28. }
  29. }
  30. }
  31. }

上述标量函数理应在迁移时生成,现在我们首先在上下文构造函数中创建即在运行时创建。在数据库中函数中的标量函数中将生成UdfAverage函数,如下:

接下来我们再来调用创建的自定义标量函数,如下:

  1. using (var context = new EFCoreDbContext())
  2. {
  3. var blogs = context.Blogs
  4. .AsNoTracking();
  5.  
  6. var result = blogs.Select(b => new BlogDTO()
  7. {
  8. Id = b.Id,
  9. Name = b.Name,
  10. Count = EFCoreDbContext.UdfAverage(b.Id)
  11. }).ToList();
  12. }

我们看看此此查询总共耗时77ms。相比上述未调用标量函数直接调用Average方法,不会翻译成SQL,所以在数据库中查询一次,然后加载到内存中再查询一次,效果显而易见。

总结

本节我们详细讲解了EF Core  2.0中的自定义标量函数,若我们需要进行子查询返回标量值时此时创建自定义标量函数将成为首选,其性能比调用内置的APi然后在内存中进行查询而不会翻译成SQL的性能更好。精简的内容,简单的讲解,希望对阅读的您有所帮助,我们明天再会。

EntityFramework Core 2.0自定义标量函数两种方式的更多相关文章

  1. iOS 自定义layer的两种方式

    在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIVi ...

  2. 自定义UITabBar的两种方式

    开发中,经常会遇到各种各样的奇葩设计要求,因为apple提供的UITabBar样式单一,只是简单的"图片+文字"样式,高度49又不可以改变.自定义UITabBar成为了唯一的出路. ...

  3. SpringBoot自定义过滤器的两种方式及过滤器执行顺序

    第一种 @WebFilter + @ServletComponentScan 注解 1.首先自定义过滤器 如下自定义过滤器 ReqResFilter 必须实现  javax.servlet.Filte ...

  4. android 自定义radiogroup的两种方式

    这里先备注下 listview+radiobutton实现  浅显易懂 http://www.haolizi.net/example/view_3312.html 在radiogoup原生态源码的基础 ...

  5. Hibernate查询返回自定义VO的两种方式

    说明:createQuery用的hql语句进行查询,createSQLQuery用sql语句查询: 前者以hibernate生成的Bean为对象装入list返回:后者则是以对象数组进行存储: 一.通过 ...

  6. .Net Core下发送WebRequest请求的两种方式

    1.使用RestSharp.NetCore 2.使用WebApi请求方式

  7. AntDesign VUE:上传组件自定义限制的两种方式(Boolean、Promise)

    AntD上传组件 AntDesign VUE文档 第一种方式 beforeUpload(file) { let isLt = true if (filesSize) { isLt = file.siz ...

  8. EntityFramework Core 2.0执行原始查询如何防止SQL注入?

    前言 接下来一段时间我们来讲讲EntityFramework Core基础,精简的内容,深入浅出,希望为想学习EntityFramework Core的童鞋提供一点帮助. EntityFramewor ...

  9. .NetCore技术研究-EntityFramework Core 3.0 Preview

    前段时间.Net Core 3.0 发布了,Entity Framework Core 3.0 也发布了Preview版.假期用了一上午大致研究了一遍,同时又体验了一把Visual Studio 20 ...

随机推荐

  1. Java 几种动态代理实现及其性能比较

    原处出之于阿里liangf Interface: package com.sunchao.jdkdyproxy; public interface Subject { void request(); ...

  2. vim编辑操作

    vim    插入模式        a    光标后        A    行尾        o    光标所在行下一行        O    光标所在行上一行        i    光标前 ...

  3. java面向对象——类

    一.类 类(class)是构造对象的模板或蓝图.由类构造(construct)对象的过程称为创建类的实例(instance). 用 java 编写的所有代码都位于某个类的内部.标准的Java 库提供了 ...

  4. python_如何实现可迭代对象和迭代器对象?

    什么是可迭代对象? 列表.字符串 for循环的本质? for循环要确保in后面的对象为可迭代对象,如何确保? iter() 方法得到一个迭代器对象 不停.__next__() 方法对迭代器对象进行迭代 ...

  5. 1.NET是什么

  6. java8大基本数据类型

    基本类型 字节数 位数 最大值 最小值 byte 1byte 8bit 2^7 - 1 -2^7 short 2byte 16bit 2^15 - 1 -2^15 int 4byte 32bit 2^ ...

  7. java枚举 用于声明持久化常量 和volley 请求头

    在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. public e ...

  8. PMS 启动流程

    1.在SystemServer中启动PackageManagerService.main 2.newPackageManagerService()并添加到ServiceManager中 3.newin ...

  9. 知识点干货—多线程同步【6】之synchronized

    "明日复明日,明日何其多. 我生待明日,万事成蹉跎. 世人若被明日累,春去秋来老将至. 朝看水东流,暮看日西坠. 百年明日能几何?请君听我明日歌. 明日复明日,明日何其多! 日日待明日,万世 ...

  10. @RequestMapping注解

    Spring MVC中用于参数绑定的注解有很多,都在org.springframework.web.bind.annotation包中,根据它们处理的request的不同内容部分可以分为四类(主要讲解 ...