[C#.NET][Entity Framework] 幾個應該避開的寫法

很多人對於操作 EF 有一些疑慮,只要夠了解他,就可以避掉效能問題、陷阱,所以我會陸陸續續整理了一些使用 EF 不小心會犯的錯誤,以便提醒自己不要犯錯

本文連結

外部連結

 


不要把 IQueryable 直接拿去跑迴圈

using (var dbContext = new NorthwindDbContext())
{
	var count = 0;
	foreach (var category in dbContext.Categories)
	{
		foreach (var product in category.Products)
		{
			if (category.CategoryID == product.CategoryID)
			{
				count++;
			}
		}
	}
	Console.WriteLine($"找到了{count}次");
}

來,我們來看一下為什麼不好,設定中斷觀察一下,第一段的 foreach 就把所有的 category 都拿回來了。

延遲執行要用 ToList 或是 for 以及其他例執行的方法,才會真的觸發 SQL 查詢

 

小心, N+1 次的查詢

第二段的 foreach,再去拿一次 Product,裡面有幾筆 category,就拿幾次的 Product

這裡是消極式載入所觸發的 SQL 命令可以這樣寫

 

using (var dbContext = new NorthwindDbContext())
{
	var queryable = from category in dbContext.Categories
					join product in dbContext.Products on category.CategoryID equals product.CategoryID
					select new { category, product };
	var count = 0;
	foreach (var item in queryable)
	{
		if (item.category.CategoryID == item.product.CategoryID)
		{
			count++;
		}
	}
	Console.WriteLine($"找到了{count}次");
}

或是這樣

using (var dbContext = new NorthwindDbContext())
{
	var categories = dbContext.Categories.Include("Products");
	var count = 0;
	foreach (var category in categories)
	{
		foreach (var product in category.Products)
		{
			if (category.CategoryID == product.CategoryID)
			{
				count++;
			}
		}
	}
	Console.WriteLine($"找到了{count}次");
}

 

小心定義 IEnumerable 型別所引發的問題

這裡把 employeeFromDb 變數的型別設計成 IEnumerable<Employee>

using (var dbContext = new NorthwindDbContext())
{
	IEnumerable<Employee> employeesFromDb = from employee in dbContext.Employees
						where employee.BirthDate > new DateTime(1900, 1, 1)
						select employee;
	
	var employeesFromMemory = from employee in employeesFromDb
				  orderby employee.EmployeeID
				  select employee;

	employeesFromMemory.Dump("排序在Memory排");
}

這有甚麼問題?

可以看到應用程式並沒有送出 order by 的命令

因為 employeesFromDb 定義成 IEnumerable<Employee>,所以 employeesFromDb 變成 LINQ to Objects,就不會再套用後面的 EF 查詢設定。

執行到 var employeesFromMemory = from employee in employeesFromDb,employeesFromDb 就會把所有的資料撈回來,然後才進行記憶體排序

 

應該怎麼寫,只要把 employeeFromDb 的型別改成 var,讓它自動判別型別 IQueryable<Employee> 即可

using (var dbContext = new NorthwindDbContext())
{
	var employeesFromDb = from employee in dbContext.Employees
			      where employee.BirthDate > new DateTime(2012,1,1)
			      select employee;

	var employeesFromMemory = from employee in employeesFromDb
				  orderby employee.EmployeeID
				  select employee;

	employeesFromMemory.Dump("排序在DB排");
}

可以看到 order by 的命令有送出了

 

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo