使用EF Core時,如果多次從數據庫中查詢一個表的同一行數據,DbContext中跟蹤(track)的實體到底有幾個呢?我們下面就分情況討論下。
數據庫
首先我們的數據庫中有一個Person表,其建表腳本如下:
CREATE TABLE [dbo].[Person]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NULL, [Age] [int] NULL, [CreateTime] [datetime] NULL, CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
其中ID是自增的主鍵,Name是一個Person的名字,Age是一個Person的年齡,CreateTime表示數據是何時創建的
其次我們的數據庫中還有一個Book表,其建表腳本如下:
CREATE TABLE [dbo].[Book]( [ID] [int] IDENTITY(1,1) NOT NULL, [PersonID] [int] NULL, [BookName] [nvarchar](50) NULL, [BookDescription] [nvarchar](50) NULL, CONSTRAINT [PK_Book] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[Book] WITH CHECK ADD CONSTRAINT [FK_Book_Person] FOREIGN KEY([PersonID]) REFERENCES [dbo].[Person] ([ID]) GO ALTER TABLE [dbo].[Book] CHECK CONSTRAINT [FK_Book_Person] GO
其中ID是自增的主鍵,PersonID為外鍵,其對應Person表的主鍵列ID,BookName是一本書的名字,BookDescription是一本書的描述信息
Person表和Book表之間是一對多關系,外鍵FK_Book_Person,通過Person表的主鍵列ID和Book表的外鍵列PersonID關聯,為強制約束(WITH CHECK),沒有級聯更新和級聯刪除。
實體
新建一個.NET Core控制台項目,我們在EF Core中用Scaffold-DbContext指令自動生成實體。
Person實體的代碼如下:
using System; using System.Collections.Generic; namespace EFCoreMultipleSelections.Entities { public partial class Person { public Person() { Book = new HashSet<Book>(); } public int Id { get; set; } public string Name { get; set; } public int? Age { get; set; } public DateTime? CreateTime { get; set; } public ICollection<Book> Book { get; set; } } }
其每一個屬性都和數據庫Person表的列對應,導航屬性Book是ICollection<Book>類型的集合,表示一個Person實體包含多個Book實體。
Book實體的代碼如下:
using System; using System.Collections.Generic; namespace EFCoreMultipleSelections.Entities { public partial class Book { public int Id { get; set; } public int? PersonId { get; set; } public string BookName { get; set; } public string BookDescription { get; set; } public Person Person { get; set; } } }
其每一個屬性都和數據庫Book表的列對應,導航屬性Person是一個Person實體,表示一個Book實體對應一個Person實體。
Scaffold-DbContext指令生成的DbContext類TestDBContext如下:
using EFCoreMultipleSelections.Logger; using Microsoft.EntityFrameworkCore; namespace EFCoreMultipleSelections.Entities { public partial class TestDBContext : DbContext { public TestDBContext() { } public TestDBContext(DbContextOptions<TestDBContext> options) : base(options) { } public virtual DbSet<Book> Book { get; set; } public virtual DbSet<Person> Person { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseLoggerFactory(new EFLoggerFactory()); optionsBuilder.UseSqlServer("Server=localhost;User Id=sa;Password=1qaz!QAZ;Database=TestDB"); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>(entity => { entity.Property(e => e.Id).HasColumnName("ID"); entity.Property(e => e.BookDescription).HasMaxLength(50); entity.Property(e => e.BookName).HasMaxLength(50); entity.Property(e => e.PersonId).HasColumnName("PersonID"); entity.HasOne(d => d.Person) .WithMany(p => p.Book) .HasForeignKey(d => d.PersonId) .HasConstraintName("FK_Book_Person"); }); modelBuilder.Entity<Person>(entity => { entity.Property(e => e.Id).HasColumnName("ID"); entity.Property(e => e.CreateTime).HasColumnType("datetime"); entity.Property(e => e.Name).HasMaxLength(50); }); } } }
可以看到沒什么特別的,與數據庫中Person表和Book表一致,Fluent API設置了它們之間的一對多關系和外鍵。
測試
.NET Core控制台項目中Program類的代碼如下:
using EFCoreMultipleSelections.Entities; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; namespace EFCoreMultipleSelections { class Program { /// <summary> /// 初始化數據庫Person表和Book表的數據,刪除數據庫中Person表和Book表的老數據,並重新插入三條Person數據和三條Book數據 /// </summary> static void InitData() { using (TestDBContext dbContext = new TestDBContext()) { dbContext.Database.ExecuteSqlCommand("DELETE FROM [dbo].[Book]"); dbContext.Database.ExecuteSqlCommand("DELETE FROM [dbo].[Person]"); var jim = new Person() { Name = "Jim", Age = 20, CreateTime = DateTime.Now }; var tom = new Person() { Name = "Tom", Age = 25, CreateTime = DateTime.Now }; var bill = new Person() { Name = "Bill", Age = 30, CreateTime = DateTime.Now }; //Tom擁有三本書 tom.Book = new List<Book>() { new Book(){ BookName="Chinese", BookDescription="Chinese"}, new Book(){ BookName="English", BookDescription="English"}, new Book(){ BookName="Japanese", BookDescription="Japanese"} }; dbContext.Person.Add(jim); dbContext.Person.Add(tom); dbContext.Person.Add(bill); dbContext.SaveChanges(); } } /// <summary> /// 兩次從數據庫中查詢Person實體tom,更改其Age屬性值 /// </summary> static void SelectData() { using (TestDBContext dbContext = new TestDBContext()) { var tom = dbContext.Person.First(p => p.Name == "Tom");//第一次從數據庫中查詢Person實體tom Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於此時Person實體tom才從數據庫中被查出來,所以此時其EntityState為Unchanged Console.WriteLine($"Tom's age is : {tom.Age.ToString()} before change");//此時Person實體tom的Age輸出為25,和數據庫初始化數據一樣 tom.Age = tom.Age + 10;//更改Person實體tom的Age,增加10 Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出為35,為加10后的值 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//調用DbContext.Entry()方法后,DbContext發現了Person實體tom的Age值已經被更改,所以這里Person實體tom的EntityState為Modified dbContext.Database.ExecuteSqlCommand("UPDATE [dbo].[Person] SET AGE=AGE+30 WHERE [Name]=N'Tom'");//執行這行代碼后,數據庫Person表中,Tom的Age會變為55 Console.WriteLine(); var tomAgain = dbContext.Person.First(p => p.Name == "Tom");//第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出還是為35,並不是數據庫中Person表的實際值55 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//此時Person實體tom的EntityState還是為Modified Console.WriteLine($"TomAgin's age is : {tomAgain.Age.ToString()}");//Person實體tomAgain的Age輸出為35,並不是數據庫中Person表的實際值55 Console.WriteLine($"TomAgin's entity state is : {dbContext.Entry(tomAgain).State.ToString()}");//Person實體tomAgain的EntityState為Modified Console.WriteLine($"Whether Tom is TomAgin ? {(tom == tomAgain).ToString()}");//輸出為true,表明tom和tomAgain指向的是同一個實體對象 } } /// <summary> /// 兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,將Person實體tom的EntityState設置回Unchanged /// </summary> static void SelectDataWithUnchanged() { using (TestDBContext dbContext = new TestDBContext()) { var tom = dbContext.Person.First(p => p.Name == "Tom");//第一次從數據庫中查詢Person實體tom Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於此時Person實體tom才從數據庫中被查出來,所以此時其EntityState為Unchanged Console.WriteLine($"Tom's age is : {tom.Age.ToString()} before change");//此時Person實體tom的Age輸出為25,和數據庫初始化數據一樣 tom.Age = tom.Age + 10;//更改Person實體tom的Age,增加10 Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出為35,為加10后的值 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//調用DbContext.Entry()方法后,DbContext發現了Person實體tom的Age值已經被更改,所以這里Person實體tom的EntityState為Modified dbContext.Database.ExecuteSqlCommand("UPDATE [dbo].[Person] SET AGE=AGE+30 WHERE [Name]=N'Tom'");//執行這行代碼后,數據庫Person表中,Tom的Age會變為55 Console.WriteLine(); dbContext.Entry(tom).State = EntityState.Unchanged;//更改Person實體tom的EntityState為Unchanged Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after unchanged");//此時Person實體tom的Age屬性值變回25,為第一次剛從數據庫中查詢出來時的值(var tom = dbContext.Person.First(p => p.Name == "Tom")) Console.WriteLine(); var tomAgain = dbContext.Person.First(p => p.Name == "Tom");//第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after second query");//此時Person實體tom的Age輸出還是為25,並不是數據庫中Person表的實際值55 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//此時Person實體tom的EntityState還是為Unchanged Console.WriteLine($"TomAgin's age is : {tomAgain.Age.ToString()}");//Person實體tomAgain的Age輸出為25,並不是數據庫中Person表的實際值55 Console.WriteLine($"TomAgin's entity state is : {dbContext.Entry(tomAgain).State.ToString()}");//Person實體tomAgain的EntityState為Unchanged Console.WriteLine($"Whether Tom is TomAgin ? {(tom == tomAgain).ToString()}");//輸出為true,表明tom和tomAgain指向的是同一個實體對象 } } /// <summary> /// 兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,調用DbContext.Attach方法,將Person實體tom重新Attach到DbContext被跟蹤的實體集合中 /// </summary> static void SelectDataWithAttach() { using (TestDBContext dbContext = new TestDBContext()) { var tom = dbContext.Person.First(p => p.Name == "Tom");//第一次從數據庫中查詢Person實體tom Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於此時Person實體tom才從數據庫中被查出來,所以此時其EntityState為Unchanged Console.WriteLine($"Tom's age is : {tom.Age.ToString()} before change");//此時Person實體tom的Age輸出為25,和數據庫初始化數據一樣 tom.Age = tom.Age + 10;//更改Person實體tom的Age,增加10 Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出為35,為加10后的值 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//調用DbContext.Entry()方法后,DbContext發現了Person實體tom的Age值已經被更改,所以這里Person實體tom的EntityState為Modified dbContext.Database.ExecuteSqlCommand("UPDATE [dbo].[Person] SET AGE=AGE+30 WHERE [Name]=N'Tom'");//執行這行代碼后,數據庫Person表中,Tom的Age會變為55 Console.WriteLine(); dbContext.Attach(tom);//調用DbContext.Attach方法,將Person實體tom重新Attach到DbContext被跟蹤的實體集合中 Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after attach");//此時Person實體tom的Age輸出還是為35,為加10后的值 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()} after attach"); //由於上面調用了DbContext.Attach方法,此時Person實體tom的EntityState變回Unchanged Console.WriteLine(); var tomAgain = dbContext.Person.First(p => p.Name == "Tom");//第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after second query");//此時Person實體tom的Age輸出還是為35,並不是數據庫中Person表的實際值55 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//此時Person實體tom的EntityState還是為Unchanged Console.WriteLine($"TomAgin's age is : {tomAgain.Age.ToString()}");//Person實體tomAgain的Age輸出為35,並不是數據庫中Person表的實際值55 Console.WriteLine($"TomAgin's entity state is : {dbContext.Entry(tomAgain).State.ToString()}");//Person實體tomAgain的EntityState為Unchanged Console.WriteLine($"Whether Tom is TomAgin ? {(tom == tomAgain).ToString()}");//輸出為true,表明tom和tomAgain指向的是同一個實體對象 } } /// <summary> /// 兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,將Person實體tom從DbContext被跟蹤的實體集合中Detach掉 /// </summary> static void SelectDataWithDetach() { using (TestDBContext dbContext = new TestDBContext()) { var tom = dbContext.Person.First(p => p.Name == "Tom");//第一次從數據庫中查詢Person實體tom Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於此時Person實體tom才從數據庫中被查出來,所以此時其EntityState為Unchanged Console.WriteLine($"Tom's age is : {tom.Age.ToString()} before change");//此時Person實體tom的Age輸出為25,和數據庫初始化數據一樣 tom.Age = tom.Age + 10;//更改Person實體tom的Age,增加10 Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出為35,為加10后的值 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//調用DbContext.Entry()方法后,DbContext發現了Person實體tom的Age值已經被更改,所以這里Person實體tom的EntityState為Modified dbContext.Database.ExecuteSqlCommand("UPDATE [dbo].[Person] SET AGE=AGE+30 WHERE [Name]=N'Tom'");//執行這行代碼后,數據庫Person表中,Tom的Age會變為55 dbContext.Entry(tom).State = EntityState.Detached;//從DbContext被跟蹤的實體集合中Detach Person實體tom,之后DbContext不再跟蹤Person實體tom Console.WriteLine(); var tomAgain = dbContext.Person.First(p => p.Name == "Tom");//第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain Console.WriteLine($"Tom's age is : {tom.Age.ToString()} after change");//此時Person實體tom的Age輸出還是為35 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//此時Person實體tom的EntityState是Detached,因為前面我們通過改變Person實體tom的State屬性,將其從DbContext中Detach掉了 Console.WriteLine($"TomAgin's age is : {tomAgain.Age.ToString()}");//Person實體tomAgain的Age輸出為55,和數據庫中Person表的數據一致 Console.WriteLine($"TomAgin's entity state is : {dbContext.Entry(tomAgain).State.ToString()}");//因為從數據庫中被查出來后,我們沒有對Person實體tomAgain的任何屬性做更改,所以這里其EntityState為Unchanged Console.WriteLine($"Whether Tom is TomAgin ? {(tom == tomAgain).ToString()}");//輸出為false,表明tom和tomAgain指向的不是同一個實體對象 } } /// <summary> /// 三次從數據庫中查詢Person實體tom: /// 第一次查詢不使用EF Core中Eager Loading的Include方法加載Book表的數據 /// 第二次查詢使用EF Core中Eager Loading的Include方法加載Book表的數據 /// 第三次查詢不使用EF Core中Eager Loading的Include方法加載Book表的數據 /// </summary> static void SelectDataWithNavigationProperty() { using (TestDBContext dbContext = new TestDBContext()) { var tom = dbContext.Person.First(p => p.Name == "Tom");//第一次從數據庫中查詢Person實體tom,不使用EF Core中Eager Loading的Include方法加載Book表的數據 Console.WriteLine($"Tom's book count is : {tom.Book.Count.ToString()}");//由於第一次查詢Person實體tom時,沒有使用EF Core中Eager Loading的Include方法加載Book表的數據,所以Person實體tom的導航屬性Book集合中沒有任何Book實體,其Count為0 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於我們沒有對Person實體tom做任何更改,所以此時其EntityState為Unchanged Console.WriteLine(); tom = dbContext.Person.Include(p => p.Book).First(p => p.Name == "Tom");//第二次從數據庫中查詢Person實體tom,使用EF Core中Eager Loading的Include方法加載Book表的數據 Console.WriteLine($"Tom's book count is : {tom.Book.Count.ToString()}");//由於第二次查詢Person實體tom時,使用了EF Core中Eager Loading的Include方法加載Book表的數據,所以Person實體tom的導航屬性Book集合中現在有三個Book實體,其Count為3,對應數據庫Book表中的三行數據 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於我們沒有對Person實體tom做任何更改,所以此時其EntityState為Unchanged Console.WriteLine(); tom = dbContext.Person.First(p => p.Name == "Tom");//第三次從數據庫中查詢Person實體tom,不使用EF Core中Eager Loading的Include方法加載Book表的數據 Console.WriteLine($"Tom's book count is : {tom.Book.Count.ToString()}");//由於第二次查詢Person實體tom時,使用了EF Core中Eager Loading的Include方法加載Book表的數據,盡管我們第三次從數據庫中查詢Person實體tom時,沒有用Include方法,但是Person實體tom的導航屬性Book並沒有被重置,所以Person實體tom的導航屬性Book集合中現在還是有三個Book實體,其Count為3,對應數據庫Book表中的三行數據 Console.WriteLine($"Tom's entity state is : {dbContext.Entry(tom).State.ToString()}");//由於我們沒有對Person實體tom做任何更改,所以此時其EntityState為Unchanged } } static void Main(string[] args) { InitData(); SelectData(); SelectDataWithUnchanged(); SelectDataWithAttach(); SelectDataWithDetach(); SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); } } }
這就是我們的測試代碼,其中有這么幾個方法:
- InitData方法,用於初始化數據庫Person表和Book表的數據,刪除數據庫中Person表和Book表的老數據,並重新插入三條Person數據和三條Book數據
- SelectData方法,兩次從數據庫中查詢Person實體tom,更改其Age屬性值
- SelectDataWithUnchanged方法,兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,將Person實體tom的EntityState設置回Unchanged
- SelectDataWithAttach方法,兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,調用DbContext.Attach方法,將Person實體tom重新Attach到DbContext被跟蹤的實體集合中
- SelectDataWithDetach方法,兩次從數據庫中查詢Person實體tom,更改其Age屬性值,在第二次查詢前,將Person實體tom從DbContext被跟蹤的實體集合中Detach掉
- SelectDataWithNavigationProperty方法,三次從數據庫中查詢Person實體tom,第一次查詢不使用EF Core中Eager Loading的Include方法加載Book表的數據,第二次查詢使用EF Core中Eager Loading的Include方法加載Book表的數據,第三次查詢不使用EF Core中Eager Loading的Include方法加載Book表的數據
每次執行 InitData方法后,數據庫Person表的數據除了列ID和列CreateTime外,都應該如下:
每次執行 InitData方法后,數據庫Book表的數據除了列ID和列PersonID外,都應該如下:
SelectData方法測試
首先我們更改Program類Main方法的代碼如下,測試SelectData方法:
static void Main(string[] args) { InitData(); SelectData(); //SelectDataWithUnchanged(); //SelectDataWithAttach(); //SelectDataWithDetach(); //SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); }
其輸出結果如下:
這個輸出每一行結果值的解釋可以從上面的示例代碼注釋中看到,
這里主要總結下:
- 第一次從數據庫中查詢Person實體tom后,DbContext開始跟蹤Person實體tom,我們更改了其Age屬性值,所以后來Person實體tom的EntityState變為了Modified。
- 然后我們使用DbContext中的ExecuteSqlCommand方法,使用SQL的UPDATE語句更新了數據庫Person表中Tom的Age。
- 之后我們第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain, 事實證明tom和tomAgain指向的其實是同一個Person對象,這很明確地說明了,不管從數據庫中查詢Person表多少次,只要DbContext被跟蹤的實體集合中有Key屬性(即Person實體的ID屬性)相同的Person對象,那么DbContext就不會添加新的Person實體到被跟蹤的實體集合中,也不會更換老的Person跟蹤實體。因為在SelectData方法中,我們第二次查詢 Person實體tom后,Person實體tom和tomAgain指向的是同一個Person對象,而且Age屬性值還是35和數據庫中的值55已經不一致,且 Person實體tom的EntityState還是Modified,說明DbContext並沒有因為多次查詢數據庫Person表,而去更新被跟蹤實體集合中的值。
我們可以看到執行SelectData方法后,數據庫Person表中Tom的Age列已經變為了55:
SelectData方法中第一次查詢Person實體tom( var tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished ===============================
SelectData方法中第二次查詢Person實體tom( var tomAgain = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到又開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (16ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished ===============================
SelectDataWithUnchanged方法測試
接着我們更改Program類Main方法的代碼如下,測試SelectDataWithUnchanged方法:
static void Main(string[] args) { InitData(); //SelectData(); SelectDataWithUnchanged(); //SelectDataWithAttach(); //SelectDataWithDetach(); //SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); }
其輸出結果如下:
這個輸出每一行結果值的解釋同樣可以從上面的示例代碼注釋中看到,
這里還是主要總結下:
- 第一次從數據庫中查詢Person實體tom后,DbContext開始跟蹤Person實體tom,我們更改了其Age屬性值為35,所以后來Person實體tom的EntityState變為了Modified。
- 然后我們使用DbContext中的ExecuteSqlCommand方法,使用SQL的UPDATE語句更新了數據庫Person表中Tom的Age。
- 然后我們將Person實體tom的EntityState改回了Unchanged,接着Person實體tom的Age屬性值也變回了25,這說明將一個EF Core實體的EntityState改回Unchanged后,會將其還原為才從數據庫中查詢出來的狀態,包括其所有屬性值都會被還原為剛放入DbContext中被跟蹤時的值。
- 之后我們第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain, 因為當前DbContext被跟蹤的實體集合中有相同Key屬性(即Person實體的ID屬性)的Person對象,所以tom和tomAgain指向的其實是同一個Person對象,而且Age屬性值還是為25即第一次查詢后的值,和當前數據庫中的值55不一致,且 Person實體tom的EntityState是Unchanged,說明DbContext並沒有因為多次查詢數據庫Person表,而去更新被跟蹤實體集合中的值。
我們可以看到執行SelectDataWithUnchanged方法后,數據庫Person表中Tom的Age列也變為了55:
SelectDataWithUnchanged方法中第一次查詢Person實體tom(var tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (50ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithUnchanged方法中第二次查詢Person實體tom(var tomAgain = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到又開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (35ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithAttach方法測試
接着我們更改Program類Main方法的代碼如下,測試SelectDataWithAttach方法:
static void Main(string[] args) { InitData(); //SelectData(); //SelectDataWithUnchanged(); SelectDataWithAttach(); //SelectDataWithDetach(); //SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); }
其輸出結果如下:
這個輸出每一行結果值的解釋同樣可以從上面的示例代碼注釋中看到,
這里還是主要總結下:
- 第一次從數據庫中查詢Person實體tom后,DbContext開始跟蹤Person實體tom,我們更改了其Age屬性值為35,所以后來Person實體tom的EntityState變為了Modified。
- 然后我們使用DbContext中的ExecuteSqlCommand方法,使用SQL的UPDATE語句更新了數據庫Person表中Tom的Age。
- 接着我們使用DbContext.Attach方法,將Person實體tom重新Attach到DbContext被跟蹤的實體集合中,導致其EntityState變回Unchanged,這樣做和SelectDataWithUnchanged方法中將Person實體tom的EntityState設置為Unchanged有一個最大的不同,在SelectDataWithUnchanged方法中當Person實體tom的EntityState設置為Unchanged后,其實體的所有屬性都還原為了第一次從數據庫中查詢出來的值。但是SelectDataWithAttach方法中,將Person實體tom用DbContext.Attach方法重新Attach到DbContext被跟蹤的實體集合中后,其實體的所有屬性值並沒有被還原,也就是說DbContext將Attach方法調用后的Person實體tom,認定為了新的還原點。
- 之后我們第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain, 因為當前DbContext被跟蹤的實體集合中有相同Key屬性(即Person實體的ID屬性)的Person對象,所以tom和tomAgain指向的其實是同一個Person對象,而且Age屬性值還是35和當前數據庫中的值55不一致,且 Person實體tom的EntityState是Unchanged,說明DbContext並沒有因為多次查詢數據庫Person表,而去更新被跟蹤實體集合中的值。
我們可以看到執行SelectDataWithAttach方法后,數據庫Person表中Tom的Age列也變為了55:
SelectDataWithAttach方法中第一次查詢Person實體tom(var tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (46ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithAttach方法中第二次查詢Person實體tom(var tomAgain = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到又開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (42ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithDetach方法測試
接着我們更改Program類Main方法的代碼如下,測試SelectDataWithDetach方法:
static void Main(string[] args) { InitData(); //SelectData(); //SelectDataWithUnchanged(); //SelectDataWithAttach(); SelectDataWithDetach(); //SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); }
其輸出結果如下:
這個輸出每一行結果值的解釋同樣可以從上面的示例代碼注釋中看到,
這里還是主要總結下:
- 第一次從數據庫中查詢Person實體tom后,DbContext開始跟蹤Person實體tom,我們更改了其Age屬性值,所以后來Person實體tom的EntityState變為了Modified。
- 然后我們使用DbContext中的ExecuteSqlCommand方法,使用SQL的UPDATE語句更新了數據庫Person表中Tom的Age。
- 然后我們將第一次從數據庫中查詢出的Person實體tom從DbContext中Detach掉,DbContext被跟蹤的實體集合中不再包含Name屬性為Tom的對象。
- 之后我們第二次從數據庫中查詢Person實體tom,但賦值給另一個變量tomAgain,由於第二次查詢時,DbContext被跟蹤的實體集合中沒有Name屬性為Tom的對象(其實是根據Person實體的Key屬性ID來判斷的),所以現在第二次從數據庫中被查詢出來的Person實體tomAgain為一個新的Person對象,並被放入DbContext被跟蹤的實體集合中,開始跟蹤,Person實體tomAgain的EntityState為Unchanged。
- 由於Person實體tomAgain為從數據庫Person表中查詢出的新 Person對象,所以其Age屬性值為55和數據庫值一致。
- 由於Person實體tom和tomAgain現在指向的是不同的 Person對象,所以tom == tomAgain返回false
我們可以看到執行SelectDataWithDetach方法后,數據庫Person表中Tom的Age列也變為了55:
SelectDataWithDetach方法中第一次查詢Person實體tom(var tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished ===============================
SelectDataWithDetach方法中第二次查詢Person實體tom(var tomAgain = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到又開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'localhost'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Context 'Person' started tracking 'TestDBContext' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values. =============================== EF Core log finished ===============================
SelectDataWithNavigationProperty方法測試
接着我們更改Program類Main方法的代碼如下,測試SelectDataWithNavigationProperty方法:
static void Main(string[] args) { InitData(); //SelectData(); //SelectDataWithUnchanged(); //SelectDataWithAttach(); //SelectDataWithDetach(); SelectDataWithNavigationProperty(); Console.WriteLine(); Console.WriteLine("Press any key to quit..."); Console.ReadKey(); }
其輸出結果如下:
這個輸出每一行結果值的解釋同樣可以從上面的示例代碼注釋中看到,
這里還是主要總結下:
- 我們在第一次從數據庫中查詢Person實體tom時,由於沒有使用EF Core中Eager Loading的Include方法加載Book表的數據,所以Person實體tom的導航屬性Book集合中沒有任何Book實體(可以看到即便是沒有使用Include方法來加載Book表的數據,Person實體tom的導航屬性Book也並不為null,是一個長度為0的集合,但是如果是非集合的導航屬性,例如Book實體的Person屬性,在沒有使用Include方法,也沒有開啟Lazy Loading的情況下就為null了),其Count為0。並且Person實體tom的EntityState為Unchanged。
- 第二次從數據庫中查詢Person實體tom時,由於使用了EF Core中Eager Loading的Include方法加載Book表的數據,所以Person實體tom的導航屬性Book集合中現在有三個Book實體,其Count為3,可以看到即便是第一次查詢Person實體tom時沒有加載導航屬性Book,第二次查詢Person實體tom時也可以用Include方法來追加其導航屬性Book。
- 第三次從數據庫中查詢Person實體tom時,即便沒有使用EF Core中Eager Loading的Include方法加載Book表的數據,但是Person實體tom的導航屬性Book集合中還是有三個Book實體,其Count為3,說明一旦導航屬性被前面的查詢加載之后,接下來的查詢不管是否使用Include方法,都會保存Person實體tom的導航屬性Book集合的值,不會因為接下來的查詢沒有使用Include方法而將其清空。
SelectDataWithNavigationProperty方法中第一次從數據庫中查詢Person實體tom(var tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (41ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithNavigationProperty方法中第二次從數據庫中查詢Person實體tom(tom = dbContext.Person.Include(p => p.Book).First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢,其中不僅查詢了Person表,還使用了SQL中的Join來查詢了Book表的數據:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' ORDER BY [p].[ID] =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (39ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' ORDER BY [p].[ID] =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [p.Book].[ID], [p.Book].[BookDescription], [p.Book].[BookName], [p.Book].[PersonID] FROM [Book] AS [p.Book] INNER JOIN ( SELECT TOP(1) [p0].[ID] FROM [Person] AS [p0] WHERE [p0].[Name] = N'Tom' ORDER BY [p0].[ID] ) AS [t] ON [p.Book].[PersonID] = [t].[ID] ORDER BY [t].[ID] =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (36ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [p.Book].[ID], [p.Book].[BookDescription], [p.Book].[BookName], [p.Book].[PersonID] FROM [Book] AS [p.Book] INNER JOIN ( SELECT TOP(1) [p0].[ID] FROM [Person] AS [p0] WHERE [p0].[Name] = N'Tom' ORDER BY [p0].[ID] ) AS [t] ON [p.Book].[PersonID] = [t].[ID] ORDER BY [t].[ID] =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Context 'Book' started tracking 'TestDBContext' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Context 'Book' started tracking 'TestDBContext' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Context 'Book' started tracking 'TestDBContext' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================
SelectDataWithNavigationProperty方法中第三次從數據庫中查詢Person實體tom(tom = dbContext.Person.First(p => p.Name == "Tom"))時EF Core后台生成的日志如下,可以看到開啟了一個數據庫連接做查詢:
=============================== EF Core log started =============================== Opening connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Opened connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Executed DbCommand (28ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [p].[ID], [p].[Age], [p].[CreateTime], [p].[Name] FROM [Person] AS [p] WHERE [p].[Name] = N'Tom' =============================== EF Core log finished =============================== =============================== EF Core log started =============================== A data reader was disposed. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closing connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished =============================== =============================== EF Core log started =============================== Closed connection to database 'TestDB' on server 'CNGDCAAITSQL01'. =============================== EF Core log finished ===============================