EntityFramework Core 2.0自定义标量函数两种方式

前言

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

自定义标量函数两种方式

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

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

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

自定义标量函数方式一

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

        [DbFunction]
        public static string ScalarFunction(string name)
        {
            throw new NotImplementedException();
        }

自定义标量函数方式二

        public static string ScalarFunction(string name)
        {
            throw new NotImplementedException();
        }

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

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

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

        [DbFunction(FunctionName = "UdfFunction", Schema = "dbo")]
        public static string ScalarFunction(string name)
        {
            throw new NotImplementedException();
        }

或者

        public static string ScalarFunction(string name)
        {
            throw new NotImplementedException();
        }

        modelBuilder.HasDbFunction(
            () => ScalarFunction(null)).HasName("UdfFunction").HasSchema("dbo");
        或者
        modelBuilder.HasDbFunction(GetType()
            .GetMethod("ScalarFunction"), options =>
            {
                options.HasName("UdfFunction");
                options.HasSchema("dbo");
            });

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

            using (var context = new EFCoreDbContext())
            {
                var blogs = context.Blogs
                  .AsNoTracking();

                var result = blogs.Select(b => new BlogDTO()
                {
                    Id = b.Id,
                    Name = b.Name,
                    Count = b.Posts.Count() > 0 ? b.Posts.Average(d => d.CommentCount) : 0
                }).ToList();
            }

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

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

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

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

    public static class AddUdfHelper
    {
        public static void AddUdfToDatabase(this DbContext context)
        {
            using (var transaction = context.Database.BeginTransaction())
            {
                try
                {
                    context.Database.ExecuteSqlCommand(
                        "IF OBJECT_ID(‘dbo.UdfAverage‘, N‘FN‘) IS NOT NULL " +
                        "DROP FUNCTION dbo.UdfAverage");

                    context.Database.ExecuteSqlCommand(
                        "CREATE FUNCTION UdfAverage (@blogId int)" +
                        @"  RETURNS FLOAT
  AS
  BEGIN
  DECLARE @result AS FLOAT
  SELECT @result = AVG(CAST([CommentCount] AS FLOAT)) FROM dbo.Posts AS p
       WHERE p.BlogId = @blogId
  RETURN @result
  END");
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }

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

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

            using (var context = new EFCoreDbContext())
            {
                var blogs = context.Blogs
                  .AsNoTracking();

                var result = blogs.Select(b => new BlogDTO()
                {
                    Id = b.Id,
                    Name = b.Name,
                    Count = EFCoreDbContext.UdfAverage(b.Id)
                }).ToList();
            }

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

总结

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

原文地址:https://www.cnblogs.com/CreateMyself/p/8485697.html

时间: 2024-08-05 04:21:03

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

iOS 自定义layer的两种方式

在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个图层 @property(nonatomic,readonly,retain) CALayer *layer; 每一个UIView内部都默认关联一个CALayer,我们可称这个Laye

自定义UITabBar的两种方式

开发中,经常会遇到各种各样的奇葩设计要求,因为apple提供的UITabBar样式单一,只是简单的"图片+文字"样式,高度49又不可以改变.自定义UITabBar成为了唯一的出路.下面我就列举开发中我经常用到的两种自定义UITabBar的方式,并且通过比较他们的不同之处,能够知道何时用何种方式自定义UITabBar. 方式一: 这是真正意义上的自定义UITabBar,因为这种方式需要继承自UITabBar,但是缺点也很明显,高度永远是49,实际开发的项目中的tabBar如果和原生的UI

【iOS开发-图层】自定义图层的两种方式

想要自定义图层,只需要构建一个类继承CALayer方法 如果让自定义图层初始化上面就有画好的图形,有两种办法 重写drawInContext方法 自定义的图层下面的方法,然后必须自定义的图层对象显示调用 [layer setNeedsDisplay]然后才会调用这个方法; 自定图层文件 - (void)drawInContext:(CGContextRef)ctx { CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);//设置红色 CGContextAddEll

自定义View的两种方式

(一)xib自定义view 1.思路 1)首先创建AppView类是,勾选上xib,就会创建出AppView.h,AppView.m,AppView.xib三个文件 2)先在AppView.xib文件上拖拽相应的控件,并进行适配 3)同样在AppView.h文件中,提供一个类方法创建AppView对象,并添加一个数据模型属性,用于向AppView传递模型 4)在AppView.m文件中,实现类方法,并在数据模型的set方法中实现对成员view内容的设置更新,将需要用到的空间属性通过拖线到.m文件

android 自定义radiogroup的两种方式

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

SQL Server如何定位自定义标量函数被那个SQL调用次数最多浅析

前阵子遇到一个很是棘手的问题,监控系统DPA发现某个自定义标量函数被调用的次数非常高,高到一个离谱的程度.然后在Troubleshooting这个问题的时候,确实遇到了一些问题让我很是纠结,下文是解决问题过程的一点思索和尝试,如果你有更好的思路和解决方法,也请多多指教. DPA可以监控到该函数每小时被调用的次数,如下截图所示: 那么第一个问题来了. DPA如何监控获取这个函数每小时执行多少次呢? 其实这个很简单, sys.dm_exec_query_stats视图里面有个字段execution_

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

前言 接下来一段时间我们来讲讲EntityFramework Core基础,精简的内容,深入浅出,希望为想学习EntityFramework Core的童鞋提供一点帮助. EntityFramework Core执行原始查询 在EntityFramework Core中执行原始查询我们借助FromSql来实现,如下: using (var context = new EFCoreDbContext()) { var orders = context.Orders .FromSql("SELECT

转载:Flash AS3.0 加载外部资源(图片,MP3,SWF)的两种方式

Flash AS3.0 加载外部资源(图片,MP3,SWF)的两种方式 出自:http://www.cnblogs.com/top5/archive/2012/08/04/2623464.html 关于AS3加载图片等外部资源,我常用的有两种方式:Loader和Embed,各自的特点,总结如下: Embed:在Flash编译阶段就将资源嵌入,也就是说图片资源是会被编译进swf文件里面去的:这样子的好处是代码比较简洁,使用方便,也不存在加载顺序的问题,缺点是会增大swf文件的体积 Loader:在

partition函数两种实现方法

patition函数根据某种比较关系将数组分成两部分,下面根据元素比某个数字大或小,以此为基准划分,给出两种实现方式 1)若数组为a[0]~a[n-1],函数调用如下 partition(a,-1,n-1)a[n-1]一般作为基准元素所在的位置,返回基准元素应该放置的下标 int partition(int *a, int i, int j, int pivot){ do{ while (a[++i] < pivot); while ((j > 0) && (a[--j] &g