• EntityFramework Core Raw SQL


    前言

    本节我们来讲讲EF Core中的原始查询,目前在项目中对于简单的查询直接通过EF就可以解决,但是涉及到多表查询时为了一步到位就采用了原始查询的方式进行。下面我们一起来看看。

    EntityFramework Core Raw SQL

    基础查询(执行SQL和存储过程)

    啥也不说了,拿起键盘就是干,如下:

        public class HomeController : Controller
        {
            private IBlogRepository _blogRepository;
            public HomeController(IBlogRepository blogRepository)
            {
                _blogRepository = blogRepository;
            }
            public IActionResult Index()
            {
                var list = _blogRepository.GetList();
                return Ok();
            }
        }
        public class BlogRepository : EntityBaseRepository<Blog>,
            IBlogRepository
        {
            private EFCoreContext _efCoreContext;
            public BlogRepository(EFCoreContext efCoreContext) : base(efCoreContext)
            {
                _efCoreContext = efCoreContext;
            }
    
            public IEnumerable<Blog> GetList()
            {
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("select * from Blog");
                return iQueryTable.ToList();
            }
        }

    下面我们来看看存储过程。

    CREATE PROCEDURE dbo.GetBlogList
    AS
    BEGIN
        SELECT * FROM dbo.Blog
    END
    GO
            public IEnumerable<Blog> GetList()
            {
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("EXECUTE  dbo.GetBlogList");
                return iQueryTable.ToList();
            }

    参数查询 

    利用参数化存储过程查询。

    ALTER PROCEDURE [dbo].[GetBlogList]
    @id INT
    AS BEGIN SELECT
    * FROM dbo.Blog WHERE Id = @id END

    结果利用FromSql就变成了如下:

            public IEnumerable<Blog> GetList()
            {
                var Id = new SqlParameter("Id", "1");
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("EXEC dbo.GetBlogList {0}", 1);
                return iQueryTable.ToList();
            }

    上述是利用string.format的形式来传参,我们也可以利用SqlParameter来传参,如下:

            public IEnumerable<Blog> GetList()
            {
                var Id = new SqlParameter("Id", "1");
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("EXEC dbo.GetBlogList @id", Id);
                return iQueryTable.ToList();
            }

    我们通过开启调试,可以清晰看到执行的存储过程。

    通过如上我们知道参数化查询有两种形式,下面我们再来看看linq查询。

    linq查询

    上述我们演示一直直接使用FromSql,其实在此之后我们可以继续通过linq来进行查询,如下:

            public IEnumerable<Blog> GetList()
            {
                var Id = new SqlParameter("Id", "2");
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("EXEC dbo.GetBlogList @id", Id).Where(d => d.Name == "efcore2");
                return iQueryTable.ToList();
            }

    之前我们映射了Blog和Post之间的关系,这里我们只能查询出Blog表的数据,通过对上述linq的讲解,我们完全可以通过inlcude来显式加载Post表数据,如下:

            public IEnumerable<Blog> GetList()
            {
                var Id = new SqlParameter("Id", "2");
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("EXEC dbo.GetBlogList @id", Id).Include(d => d.Posts);
                return iQueryTable.ToList();
            }

    好吧,明确告诉我们对于存储过程是不支持Inlude操作的,所以要想Include我们只能进行简单的查询,如下:

            public IEnumerable<Blog> GetList()
            {
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("select * from blog").Include(d => d.Posts);
                return iQueryTable.ToList();
            }

    查找官网资料时发现居然对表值函数(TVF)是可以Include的,创建内嵌表值函数如下:

    USE [EFCoreDb]
    GO
    
    IF OBJECT_ID('dbo.GetBlog') IS NOT NULL
        DROP FUNCTION dbo.GetBlog;
    GO
    
    CREATE FUNCTION dbo.GetBlog 
        (@Name VARCHAR(max)) RETURNS TABLE WITH SCHEMABINDING
    AS   
    RETURN
     SELECT Id, Name, Url FROM dbo.Blog WHERE Name = @Name
    GO

    调用如下:

            public IEnumerable<Blog> GetList()
            {
                var name = "efcore2";
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("select * from [dbo].[GetBlog] {0}", name).Include(d => d.Posts);
                return iQueryTable.ToList();
            }

    结果出乎意料的出现语法错误:

    通过SQL Server Profiler查看发送的SQL语句如下:

    这能不错么,官网给的示例也是和上述一样,如下:

    只是按照和他一样的搬过来了,未曾想太多,还是粗心大意了,想了好一会,按照我们正常调用表值函数即可,我们需要用括号括起来才行,如下:

            public IEnumerable<Blog> GetList()
            {
                var name = "efcore2";
                var iQueryTable = _efCoreContext.Set<Blog>().
                    FromSql("select * from [dbo].[GetBlog] ({0})", name).Include(d => d.Posts);
                return iQueryTable.ToList();
            }

    上述将[dbo.GetBlog]和({0})隔开和挨着都可以。这个时候才不会出现语法错误。执行的SQL如下才是正确的。

    好了,到了这里关于EF Core中原始查询我们就告一段落了,其中还有一个知识点未谈及到,在EF Core我们可以直接通过底层的ADO.NET来进行查询,我们来看下:

    底层ADO.NET查询

            public IEnumerable<Blog> GetList()
            {
                var list = new List<Blog>();
                using (var connection = _efCoreContext.Database.GetDbConnection())
                {
                    connection.Open();
    
                    using (var command = connection.CreateCommand())
                    {
                        command.CommandText = "SELECT * FROM dbo.Blog";
    
                        using (SqlDataReader reader = command.ExecuteReader() as SqlDataReader)
                        {
                            while (reader.Read())
                            {
                                var blog = new Blog();
                                blog.Id = Convert.ToInt32(reader["Id"]);
                                blog.Name = reader["Name"].ToString();
                                blog.Url = reader["Url"].ToString();
                                list.Add(blog);
                            }
                        }                      
                    }
                }
                return list;
            }

    总结

    我们本节讲述了EF Core中的原始查询,相比较之前EF版本的原始查询使用更加灵活了一点,但是缺陷还是展露无遗,依然只能查出所有的列且必须匹配,同时呢,若我们想执行事务,目前还不支持底层的TranscationScope仅仅支持BeginTranscation。

  • 相关阅读:
    一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?
    接口中定义的变量为什么是常量
    关于String s = new String("xyz");创建了几个字符串对象?的问题
    java面试题之----JVM架构和GC垃圾回收机制详解
    Object中的clone方法
    C/S与B/S架构的区别和优缺点
    EJB是什么?
    JNDI是什么,怎么理解
    java中什么是上下文(servletContext)
    java面试题----String、StringBuffer、StringBudder区别
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/6254241.html
Copyright © 2020-2023  润新知