1. 程式人生 > >EF6學習筆記十:原始查詢,在EF中使用SQL語句

EF6學習筆記十:原始查詢,在EF中使用SQL語句

麻將 list() eat write 我們 ace fir esp etime

要專業系統地學習EF前往《你必須掌握的Entity Framework 6.x與Core 2.0》這本書的作者(汪鵬,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/

EF裏面當然也可以直接使用SQL語句了,比如有些復雜的查詢用LINQ寫不了的,還有存儲過程那些東西。

EF為查詢操作提供了兩個方法:ctx.Database.SqlQuery<T>()、ctx.DbSet<T>.SqlQuery() (ctx表示上下文對象)

Insert、Update、Delete 操作提供了兩個方法:ExecuteSqlCommand()、ExecuteSqlCommandAsync()

我們來弄一弄這些方法,看看怎麽回事

原始查詢

ctx.Database.SqlQuery<T>() 和 ctx.Dbset<T>.SqlQuery() 兩個方法的區別,最先要說的就是,ctx.Database.SqlQuery<T>()查詢出的數據沒有被上下文追蹤,另一個方法查詢出的實體則被追蹤了

來看ctx.Database.SqlQuery<T>() 查詢出實體的狀態為Detached

技術分享圖片
using(EFDbContext ctx = new EFDbContext)
{
      var res = ctx.Database.SqlQuery<Product>("
select *from tb_products"); //var state = ctx.Entry(res).State; // 報錯 實體 DbRawSqlQuery不是上下文模型的一部分 var first = res.FirstOrDefault(); var state = ctx.Entry(first).State; Console.WriteLine(state); // Deteched }
View Code

來看ctx.Dbset<T>.SqlQuery() 實體狀態為Unchanged

技術分享圖片
var res = ctx.Products.SqlQuery("
select * from tb_products"); var pro = res.FirstOrDefault(); var state = ctx.Entry(pro).State; Console.WriteLine(state); // Unchanged
View Code

這是他們之間的第一個區別,不過這個倒沒什麽大礙,對吧。即使不被跟蹤,我也可以調用Attach方法對它進行追蹤

查詢指定列的數據(大於1,小於總列數)

這兩個方法不支持查詢指定某幾列的數據,必須要所有列的數據

來看SqlQuery<T>()

技術分享圖片
                var res = ctx.Database.SqlQuery<Product>("select id,name from tb_products");
                var product = res.FirstOrDefault();
// 報錯:System.Data.Entity.Core.EntityCommandExecutionException: The data reader is incompatible with the specified ‘CodeFirstNamespace.Product‘. A member of the type, ‘Price‘, does not have a corresponding column in the data reader with the same name.
View Code

來看SqlQuery()

技術分享圖片
var res = ctx.Products.SqlQuery("select id,name from tb_products");
var product = res.FirstOrDefault();
//  報錯:System.Data.Entity.Core.EntityCommandExecutionException: The data reader is incompatible with the specified ‘CodeFirstNamespace.Product‘. A member of the type, ‘Price‘, does not have a corresponding column in the data reader with the same name.
View Code

連接查詢

上面的問題是,他必須要查詢所有列的數據,但是連接查詢又可以,指定某幾列,沒有問題……

但是使用連接查詢你得定義類來接收(除非你有適合的類型,哪怕是object、dynamic都不行),只要查詢出來的列和你model中的屬性數量不匹配,就會報錯

我用dynamic類型接收,不報錯,但是沒有數據

技術分享圖片
var res = ctx.Database.SqlQuery<dynamic>(@"select o.Id,o.OrderNO,p.name as ProductName from tb_Orders as o inner join tb_Products as p 
                //on o.id = p.FK_Order_Id").ToList();
                //                Console.WriteLine(JsonConvert.SerializeObject(res));  //  [{},{},{},{},{},{},{},{},{}]
View Code

用object是一樣的結果

技術分享圖片
var res = ctx.Database.SqlQuery<object>(@"select o.Id,o.OrderNO,p.name as ProductName from tb_Orders as o inner join tb_Products as p 
                //on o.id = p.FK_Order_Id").ToList();
                //                Console.WriteLine(JsonConvert.SerializeObject(res));  //  [{},{},{},{},{},{},{},{},{}]
View Code

那我不使用連接查詢呢?我就查詢三列,一樣

技術分享圖片
ctx.Database.SqlQuery<object>(@"select id,name from tb_products").ToList();
                //Console.WriteLine(JsonConvert.SerializeObject(res));  //  [{},{},{},{},{},{},{},{},{}]
View Code

那我查詢全部,也是一樣的

技術分享圖片
ctx.Database.SqlQuery<object>(@"select * from tb_products").ToList();
                //Console.WriteLine(JsonConvert.SerializeObject(res));  //  [{},{},{},{},{},{},{},{},{}]
View Code

不去了解他了

我剛剛把數量給高亮了,這就要說明一下,你使用什麽類型去接收,只要你的model屬性的數量和查詢數據集中列的數量不一致就會報錯

現在我定義一個test類

技術分享圖片
public class Test
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }
View Code

然後連接查詢兩列,ID和ProductName,用test類型去接收,可以的

技術分享圖片
//                var res = ctx.Database.SqlQuery<Test>(@"select o.Id,o.OrderNO,p.name as ProductName from tb_Orders as o inner join tb_Products as p 
                //on o.id = p.FK_Order_Id");
                //                Console.WriteLine(JsonConvert.SerializeObject(res.ToList()));
                //[{"Id":"82903023-a7a6-4839-9caa-153ee9d00e65","Name":null},{"Id":"469b82be-8139-4e67-b566-5b2b5f6d838d","Name":null},{"Id":"e18757db-1db8-4f7f-b702-79138709b304","Name":null},{"Id":"e18757db-1db8-4f7f-b702-79138709b304","Name":null},{"Id":"469b82be-8139-4e67-b566-5b2b5f6d838d","Name":null},{"Id":"e18757db-1db8-4f7f-b702-79138709b304","Name":null},{"Id":"82903023-a7a6-4839-9caa-153ee9d00e65","Name":null},{"Id":"82903023-a7a6-4839-9caa-153ee9d00e65","Name":null},{"Id":"469b82be-8139-4e67-b566-5b2b5f6d838d","Name":null}]
View Code

查詢表中單列數據

比如我們查詢表中某一列數據,或者使用Count()聚合查詢,那麽SqlQuery<T>()支持,SqlQuery()不支持

技術分享圖片
           //  查詢單條記錄
           var res = ctx.Database.SqlQuery<string>("select name from tb_products");
                
           Console.WriteLine(JsonConvert.SerializeObject(res.ToList()));
                //  ["牙刷","磚頭","蘋果","柚子","瓷磚","柑橘","嗽口水","牙膏","水泥"]
View Code 技術分享圖片
                var res3 = ctx.Products.SqlQuery("select name from tb_products");
                Console.WriteLine(JsonConvert.SerializeObject(res3));
                //  System.Data.Entity.Core.EntityCommandExecutionException: The data reader is incompatible with the specified ‘CodeFirstNamespace.Product‘. A member of the type, ‘Id‘, does not have a corresponding column in the data reader with the same name.
View Code

查詢時,傳遞參數

在查詢字符串中傳遞參數這個太需要了對吧,不安全的做法,直接拼接SQL語句;安全的做法,使用參數化查詢

先來個拼接的

技術分享圖片
//  拼接的方式
decimal price = 14m;
var products = ctx.Database.SqlQuery<Product>($"select *from tb_Products where Price = {price}");
                
Console.WriteLine(JsonConvert.SerializeObject(products.ToList()));
                //[{"Order":null,"Name":"牙刷","Price":14.00,"Unit":"只","FK_Order_Id":"82903023-a7a6-4839-9caa-153ee9d00e65","Id":"1b25351c-3008-4d27-a9de-6749ec1d0845","AddTime":"2019-01-15T10:35:03.947"}]
View Code

參數化查詢

技術分享圖片
//  參數化SQL實現
decimal price = 14m;
var parameters = new SqlParameter() { ParameterName = "@price", SqlDbType = System.Data.SqlDbType.Decimal, Value = price };
var res = ctx.Database.SqlQuery<Product>("select * from tb_products where price=@price",parameters);
               Console.WriteLine(JsonConvert.SerializeObject(res.ToList()));
//  [{"Order":null,"Name":"牙刷","Price":14.00,"Unit":"只","FK_Order_Id":"82903023-a7a6-4839-9caa-153ee9d00e65","Id":"1b25351c-3008-4d27-a9de-6749ec1d0845","AddTime":"2019-01-15T10:35:03.947"}]
View Code

原始非查詢

Insert、Update、Delete這些操作EF為我們提供了 ExecuteSqlCommand()和ExecuteSqlCommandAsync()

你一定會想到把insert語句寫到上面的SqlQuery()查詢方法中去,哈,我也想到了

技術分享圖片
var res = ctx.Products.SqlQuery("insert into tb_Products values(newid(),‘茶葉‘,55.5,‘82903023-a7a6-4839-9caa-153ee9d00e65‘,getdate(),‘斤‘);");
View Code

其實數據被添加進去了,只不過這種方式太擡杠了

最後來個一個添加,使用EF提供的正宗的方法,當然必須是參數化的方式

技術分享圖片
                //  參數化添加
                string sql = @"insert into tb_Products values(@Id,@Name,@Price,@FK_Order_Id,@AddTime,@Unit);";
                var parameterList = new List<SqlParameter> {
                    new SqlParameter("@Id",Guid.NewGuid().ToString()),
                    new SqlParameter("@Name","大米"),
                    new SqlParameter("@Price",73m),
                    new SqlParameter("@FK_Order_Id","82903023-a7a6-4839-9caa-153ee9d00e65"),
                    new SqlParameter("@AddTime",DateTime.Now),
                    new SqlParameter("@Unit","")
                };  //  這麽多@符號容易讓人一下子聯想到麻將中的一筒啊
                var parameterArr = parameterList.ToArray();
                var res = ctx.Database.ExecuteSqlCommand(sql,parameterArr);
                Console.WriteLine(res);  // result:1
View Code

技術分享圖片

是不是OK啊

EF6學習筆記十:原始查詢,在EF中使用SQL語句