Entity Framework Core 2.X Crash Tutorial (3)


这一小节主要的内容是通过观察EFCore的执行日志了解EFCore的执行过程。

将EFCore的SQL执行过程输出到Log

输出到Console:
ASPNetCore的项目已经集成好了Log工具,可以通过查看Program.cs中的CreateDefaultBuilder来查看。

 public class Program
 {
   public static void Main(string[] args)
   {
     CreateWebHostBuilder(args).Build().Run();
   }
   public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
   WebHost.CreateDefaultBuilder(args)
   .UseStartup();
 }

ASPNetCore配置了Log工具,可以输出到Debug窗口和控制台。所以ASP DotNet Core项目就不需要配置Log工具了。
CreateDefaultBuilder
如果不使用ASP DotNet Core项目,可以参考文档:https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/logging
EFCoreDoc

EFCore新增数据

增加province这个Model,当New Province的时候需要注意:Id 是作为主键。
这里需要知道这样一个约定:如果有Id或者ProvinceId,这时它是默认作为主键的,如果使用int的话,是自增的,那么新增的时候也就不用给Id赋值了。

 public class Province
 {
   public Province()
   {
     Cities = new List();
   }
   public int Id { get; set; }
   public string Name { get; set; }
   public int Population { get; set; }
   public List Cities { get; set; }
 }

正常情况我们可能是这么写,让context自动dispose:

 var province = new Province()
 {
   Name = "北京",
   Population = 20000
 };
 using (var context=new MyContext())
 {
   //Add province
 }

但是在ASP DotNetCore项目中,已经把MyContext添加到容器中了。在Startup.cs中:

 public void ConfigureServices(IServiceCollection services)
 {
   services.Configure(options =>
   {
     // This lambda determines whether user consent for non-essential cookies is needed for a given request.
     options.CheckConsentNeeded = context => true;
     options.MinimumSameSitePolicy = SameSiteMode.None;
   });
   services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
   services.AddDbContext(options =>
   {
     options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
   });
 }

因此我们可以在Controller的构造函数中进行注入:

 public class HomeController : Controller
 {
   private readonly MyContext context;
   public HomeController(MyContext context)
   {
      this.context = context;
   }
 }

然后直接用,不需要手动dispose,因为清理的工作由容器自动完成。添加的写法有以下两种:

public class HomeController : Controller
 {
   private readonly MyContext context;
   public HomeController(MyContext context)
   {
     this.context = context;
   }
   public IActionResult Index()
   {
     var province = new Province()
     {
     Name = "北京",
     Population = 20000
     };
     //Method 1(recommend)
     context.Provinces.Add(province);
     //context is now Tracking procince object
     //Method 2:
     context.Add(province);
     return View();
   }
 }

到这里,province只是在内存里并且被context追踪,但是并没有插入到数据库。要执行插入动作,还需要:context.SaveChanges();
context.Province.Add(province):
MyCOntext开始追踪province对象
Context.SaveChanges():
检查所有MyContext正在追踪的对象
读取每个对象的状态
生成SQL语句
执行所有生成的SQL语句
如果有返回数据的话,就获取这些返回数据

下面我们来打印一下SQL语句,注意修改启动方式,不使用IIS,使用控制台。
LaunchSettings
Savechanges
注意一下,Savechange()方法很有可能执行批量操作,但凡其中有一步出现错误,都会进行回滚。可以观察一下在执行过程中province的变化:
ProvinceObject1
ProvinceObject2
传入参数Name Population在日志中是加密敏感数据的,但是在开发的时候我们也许会想看一下这些传入的参数。
ParamsInLogSecret
因此,可以在依赖注入的时候配置参数options参数,允许敏感数据记录EnableSensitiveDataLogging。

 public void ConfigureServices(IServiceCollection services)
 {
   services.Configure(options =>
   {
     // This lambda determines whether user consent for non-essential cookies is needed for a given request.
     options.CheckConsentNeeded = context => true;
     options.MinimumSameSitePolicy = SameSiteMode.None;
   });
   services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
   services.AddDbContext(options =>
   {
     options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
     options.EnableSensitiveDataLogging(true);
   });
 }

这个时候我们再观察一下日志记录的结果,可以看到输入的参数:北京 20000
Beijing20000
SQLServerData

EFCore批量添加数据

批量添加,有两种方法,如下:

 public IActionResult Index()
 {
   var province1 = new Province()
   {
     Name = "上海",
     Population = 700000
   };
   var province2 = new Province()
   {
     Name = "天津",
     Population = 300000
   };
   var province3 = new Province()
   {
     Name = "广东",
     Population = 900000
   };

   //Method 1
   //context.Provinces.AddRange(province1, province2, province3);

   //Method 2
   context.Provinces.AddRange(new List
   {
     province1,province2,province1
   });
   context.SaveChanges();
   return View();

 }

通过观察日志,这两种方法的效果是一样的,都是3个Insert。
provincesInsert
下面批量添加没有关系的Company和Province的两个对象,直接用context.AddRange()这个方法。

 public IActionResult Index()
 {
   var province = new Province()
   {
     Name = "山东",
     Population = 700000
   };
   var company = new Company()
   {
     Name = "Taida",
     EstablishDate = new DateTime(1990,1,1),
     LegalPerson = "Secret Man"
   };
   context.AddRange(province,company);
   context.SaveChanges();
   return View();
 }

通过观察日志,EFCore分别执行了两次SQL操作。
CityProvinceInsert
批量操作的大小限制:
默认大小限制是由数据库Provider定的,SQLServer是1000个命令。
如果超出该大小限制,那么超出的部分将会做了另外的批次来执行。
在Startup.cs中的ConfigureServices中改下注入的配置:

 services.AddDbContext(options =>
 {
   options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), opts =>
   {
     opts.MaxBatchSize(20000);
   });
   options.EnableSensitiveDataLogging(true);
 });

EFCore 查询

前面都是组SQL语句,只有当遇到ToList()的时候才会真正执行查询。推荐第一种。

 public IActionResult Index()
 {
   var provinces = context.Provinces.ToList();
   //Method 1 Filter by province name
   var provinces1 = context.Provinces
   .Where(x=>x.Name=="北京")
   .ToList();

   //Method 2 Linq to SQL
   var provinces2 = (from p in context.Provinces
   where p.Name == "北京"
   select p).ToList();
   return View();
 }

我们可以去掉ToList(),发现是IQueryable类型。
遇到foreach也是查询数据库的操作。

 public IActionResult Index()
 {
   var provinces = context.Provinces
   .Where(x=>x.Name=="北京");
   foreach (var province in provinces)
   {
      //Add action
   }
   return View();
 }

对于这种情况,如果foreach里面的操作耗时较长,那么不推荐这么做。因为长时间打开数据库连接,会有风险,例如其他人正在用这个表。
所以还是推荐用tolist.


文章作者: Chaoqiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chaoqiang !
评论
 上一篇
Entity Framework Core 2.X  Crash Tutorial (4) Entity Framework Core 2.X Crash Tutorial (4)
这一小节的主要的内容是继续了解EFCore的执行过程,包括查询过滤和删除等操作。 EFCore查询过滤一般地,查询过滤我们会这么写: var provinces = context.Provinces .Where(x=>x.Name
下一篇 
Entity Framework Core 2.X  Crash Tutorial (2) Entity Framework Core 2.X Crash Tutorial (2)
这一小节主要的内容是在一个ASP Net Core Web项目中应用EFCore,下面是主要的步骤: 解决方案搭建创建解决方案,添加ASP NetCore Web 项目。选择ASP Net Core 2.2的MVC模板。添加Net Core
  目录