EFCore执行Sql语句的方法:FromSql与ExecuteSqlCommand

摘要:
前言在EFCore中执行Sql语句的方法有:FromSql和ExecuteSqlCommand;在EF6中,SqlQuery与ExecuteSqlCommand不同,而FromSql和SqlQuery非常不同。FromSql的返回值为IQueryable。因此,对于延迟加载,它可以与Linq扩展方法一起使用,但存在许多缺陷。建议在直接执行Sql语句时不要使用FromSql,但EFCore不提供SqlQuery方法。因此,SqlQuery的实现代码将发布在下面供您参考,以便在EFCore中使用。FromSql和ExecuteSqlCommand的使用测试使用SqlServer2008和SqlServerProfiler捕获Sql语句。EFCore版本为1.1.0。
前言

在EFCore中执行Sql语句的方法为:FromSql与ExecuteSqlCommand;在EF6中的为SqlQuery与ExecuteSqlCommand,而FromSql和SqlQuery有很大区别,FromSql返回值为IQueryable,因此为延迟加载的,可以与Linq扩展方法配合使用,但是有不少的坑(EFCore版本为1.1.0),直接执行Sql语句的建议不要使用FromSql,但是EFCore中并没有提供SqlQuery方法,因此下面会贴出SqlQuery的实现代码供大家参考,以便在EFCore中能使用。

FromSql和ExecuteSqlCommand的使用

测试时使用了SqlServer2008和SqlServer Profiler进行Sql语句捕捉,EFCore的版本为1.1.0。

测试的Entity Model与DbContext

public class MSSqlDBContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"data source=localhost;initial catalog=TestDB;Integrated Security=True;");
        }
        public DbSet<Person> Person { get; set; }
        public DbSet<Address> Address { get; set; }
}

    [Table(nameof(Person))]
    public class Person
    {
        public int id { get; set; }
        public string name { get; set; }
        [Column(TypeName = "datetime")]
        public DateTime? birthday { get; set; }
        public int? addrid { get; set; }
}

    [Table(nameof(Address))]
    public class Address
    {
        public int id { get; set; }
        public string fullAddress { get; set; }
        public double? lat { get; set; }
        public double? lon { get; set; }
    }

  

ExecuteSqlCommand

EFCore的ExecuteSqlCommand和EF6的一样,执行非查询的Sql语句:

var db = new MSSqlDBContext();
2             db.Database.ExecuteSqlCommand($"update {nameof(Person)} set name=@name where id=@id", new[] 
3             {
4                 new SqlParameter("name", "tom1"),
5                 new SqlParameter("id", 1),
6             });

  

FromSql

官方参考文档:https://docs.microsoft.com/en-us/ef/core/querying/raw-sql

简单使用

 var db = new MSSqlDBContext();
2             var name = "tom";
3             var list = db.Set<Person>().FromSql($"select * from {nameof(Person)} where {nameof(name)}=@{nameof(name)} ", 
4                 new SqlParameter(nameof(name), name)).ToList();

  生成的Sql:

exec sp_executesql N'select * from Person where name=@name 
',N'@name nvarchar(3)',@name=N'tom'

  

注意:

默认生成的为Person的Model,如果Select获取的字段中不包含Person中的某字段就会抛异常了,例如:下面的语句只获取name字段,并没有包含Person的其他字段,那么抛异常:The required column 'id' was not present in the results of a 'FromSql' operation.

db.Set<Person>().FromSql($"select name from {nameof(Person)} ").ToList();

  那么改为:

db.Set<Person>().Select(l => l.name).FromSql($"select name from {nameof(Person)} ").ToList();

  

执行存储过程

 var db = new MSSqlDBContext();
db.Set<Person>().FromSql("exec testproc @id", new SqlParameter("id", 1)).ToList();

  生成的Sql:

exec sp_executesql N'exec testproc @id
',N'@id int',@id=1

  

与Linq扩展方法配合使用

var db = new MSSqlDBContext();
db.Set<Person>().FromSql($"select * from {nameof(Person)} where name=@name ", new SqlParameter("@name", "tom"))
                .Select(l => new { l.name, l.birthday }).ToList();

  生成的Sql:

exec sp_executesql N'SELECT [l].[name], [l].[birthday]
FROM (
    select * from Person where name=@name 
) AS [l]',N'@name nvarchar(3)',@name=N'tom'

  

inner join + order by

var db = new MSSqlDBContext();
              (from p in db.Set<Person>().FromSql($"select * from {nameof(Person)} ")
              join a in db.Set<Address>().Where(l => true)
              on p.addrid equals a.id
              select new { p.id, p.name, a.fullAddress }).OrderBy(l => l.id).ToList();

  生成的Sql:

SELECT [p].[id], [p].[name], [t].[fullAddress]
FROM (
    select * from Person 
) AS [p]
INNER JOIN (
    SELECT [l0].*
    FROM [Address] AS [l0]
) AS [t] ON [p].[addrid] = [t].[id]
ORDER BY [p].[id]

  

left join + order by

var db = new MSSqlDBContext();
              (from p in db.Set<Person>().FromSql($"select * from {nameof(Person)} ")
              join a in db.Set<Address>().Where(l => true)
             on p.addrid equals a.id into alist
              from a in alist.DefaultIfEmpty()
              select new { p.id, p.name, fullAddress = a == null ? null : a.fullAddress }).OrderBy(l => l.id).ToList();

  生成的Sql:(生成的Sql很有问题,order by后面多了[p].[addrid],而且生成的select的字段也是多了)

SELECT [p].[id], [p].[addrid], [p].[birthday], [p].[name], [t].[id], [t].[fullAddress], [t].[lat], [t].[lon]
FROM (
    select * from Person 
) AS [p]
LEFT JOIN (
    SELECT [l0].[id], [l0].[fullAddress], [l0].[lat], [l0].[lon]
    FROM [Address] AS [l0]
) AS [t] ON [p].[addrid] = [t].[id]
ORDER BY [p].[id], [p].[addrid]

  将FromSql换成Where扩展方法试试:

             (from p in db.Set<Person>().Where(l => true)
              join a in db.Set<Address>().Where(l => true)
              on p.addrid equals a.id into alist
              from a in alist.DefaultIfEmpty()
              select new { p.id, p.name, fullAddress = a == null ? null : a.fullAddress }).OrderBy(l => l.id).ToList();

  EFCore生成的Sql(order by后面还是多了[addrid],select的字段也是多了):

SELECT [l].[id], [l].[addrid], [l].[birthday], [l].[name], [t].[id], [t].[fullAddress], [t].[lat], [t].[lon]
FROM [Person] AS [l]
LEFT JOIN (
    SELECT [l1].[id], [l1].[fullAddress], [l1].[lat], [l1].[lon]
    FROM [Address] AS [l1]
) AS [t] ON [l].[addrid] = [t].[id]
ORDER BY [l].[id], [l].[addrid]

  而在EF6中生成的Sql,比EFCore的生成好多了:

SELECT 
    [Project1].[id] AS [id], 
    [Project1].[name] AS [name], 
    [Project1].[C1] AS [C1]
    FROM ( SELECT 
        [Extent1].[id] AS [id], 
        [Extent1].[name] AS [name], 
        CASE WHEN ([Extent2].[id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE [Extent2].[fullAddress] END AS [C1]
        FROM  [dbo].[Person] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON [Extent1].[addrid] = [Extent2].[id]
    )  AS [Project1]
    ORDER BY [Project1].[id] ASC

  

结果说明

FromSql不能代替原来EF6的SqlQuery使用,而且结合Linq扩展方法使用的时候生成的Sql会存在一些问题(EFCore版本为:1.1.0),那么为了能在EFCore中执行Sql查询语句,下面提供对SqlQuery方法的实现。

SqlQuery的实现

public static IList<T> SqlQuery<T>(DbContext db, string sql, params object[] parameters)
            where T : new()
        {
            //注意:不要对GetDbConnection获取到的conn进行using或者调用Dispose,否则DbContext后续不能再进行使用了,会抛异常
            var conn = db.Database.GetDbConnection();
            try
            {
                conn.Open();
                using (var command = conn.CreateCommand())
                {
                    command.CommandText = sql;
                    command.Parameters.AddRange(parameters);
                    var propts = typeof(T).GetProperties();
                    var rtnList = new List<T>();
                    T model;
                    object val;
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            model = new T();
                            foreach (var l in propts)
                            {
                                val = reader[l.Name];
                                if (val == DBNull.Value)
                                {
                                    l.SetValue(model, null);
                                }
                                else
                                {
                                    l.SetValue(model, val);
                                }
                            }
                            rtnList.Add(model);
                        }
                    }
                    return rtnList;
                }
            }
            finally
            {
                conn.Close();
            }
        }

  使用:

var db = new MSSqlDBContext();
            string name = "tom";
            var list = SqlQuery<PAModel>(db,
                $" select p.id, p.name, a.fullAddress, a.lat, a.lon " +
                $" from ( select * from {nameof(Person)} where {nameof(name)}=@{nameof(name)} ) as p " +
                $" left join {nameof(Address)} as a on p.addrid = a.id ",
                new[] { new SqlParameter(nameof(name), name) });

  生成的Sql:

exec sp_executesql N' select p.id, p.name, a.fullAddress, a.lat, a.lon  from ( select * from Person where name=@name ) as p  left join Address as a on p.addrid = a.id ',N'@name nvarchar(3)',@name=N'tom'

  

免责声明:文章转载自《EFCore执行Sql语句的方法:FromSql与ExecuteSqlCommand》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PIE-SDK For C++打开栅格数据MySQL安装辛路下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

黑客是怎样绕过WAF之三重防护绕过讲解

什么是WAFWeb Application Firewall通过执行一系列针对HTTP/HTTPS的安全策略来防御对Web应用的攻击。目前主要有单设备WAF与云WAFWAF的现状1.太多数WAF能够拦截较为普通的WEB攻击2.大多数WAF没有针对热点漏洞奇葩攻击EXP防御的能力3.基本所有的WAF都存在策略性绕过4.由于waf的业务限制等各种原因导致存在通...

Hive 严格模式与非严格模式

1. hive严格模式 hive提供了一个严格模式,可以防止用户执行那些可能产生意想不到的不好的效果的查询。即某些查询在严格模式下无法执行。通过设置hive.mapred.mode的值为strict,可以禁止3中类型的查询。(1) 查询一个分区表时如果在一个分区表执行hive,除非where语句中包含分区字段过滤条件来显示数据范围,否则不允许执行。换句话说...

select 下拉框的样式改变

最近在做地图遇到一个很有意思的事情,就是select下拉框的样式改变 /*第一种,改变下拉框样式*/ select { /*Chrome和Firefox里面的边框是不一样的,所以复写了一下*/ border: solid 1px #000; /*很关键:将默认的select选择框样式清除*/ appearance:none; -moz-appearance...

[mybatis]list的foreach的使用

当传入参数为list的时候foreach的使用 当参数为一个list的时候 方法层: int deleteAll(List<String> list); xml文件中的sql语句 <delete parameterType="list"> delete from classify where id in <...

二十四、Mysql读写分离之Atlas

一、Atlas介绍 Atlas是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了大量bug,添加了很多功能特性。目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平台,每天承载的读写请求数达几十亿条...

innovus 自学小技巧之 gui小配置

1. innovus同时显示full_name和ref_name 2.innovus的std cell显示的颜色配置成icc颜色一样 3.innovus配置快捷键成和Icc一样 d 删除 bindKey d "deleteSelectedFromFPlan" esc 退出当前编辑状态 bindKey esc "uiSetTool select" m...