EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况
使用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(); entity.Property(e => e.BookName).HasMaxLength(); 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();
});
}
}
}
可以看到没什么特别的,与数据库中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 = , CreateTime = DateTime.Now };
var tom = new Person() { Name = "Tom", Age = , CreateTime = DateTime.Now };
var bill = new Person() { Name = "Bill", Age = , 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 + ;//更改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 + ;//更改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 + ;//更改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 + ;//更改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 ===============================
EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况的更多相关文章
- EF Core中Join可以进行子查询
我们来看看下面的代码,这个代码是一个INNER JOIN的EF Core查询,其中用SubCategory表INNER JOIN了SubCategoryLanguage表,但是我们需要在SubCate ...
- EF Core中如何正确地设置两张表之间的关联关系
数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...
- EF Core 2.0 已经支持自动生成父子关系表的实体
现在我们在SQL Server数据库中有Person表如下: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...
- EF Core中如何设置数据库表自己与自己的多对多关系
本文的代码基于.NET Core 3.0和EF Core 3.0 有时候在数据库设计中,一个表自己会和自己是多对多关系. 在SQL Server数据库中,现在我们有Person表,代表一个人,建表语句 ...
- 在ef core中使用postgres数据库的全文检索功能实战之中文支持
前言 有关通用的postgres数据库全文检索在ef core中的使用方法,参见我的上一篇文章. 本文实践了zhparser中文插件进行全文检索. 准备工作 安装插件,最方便的方法是直接使用安装好插件 ...
- EF Core中执行Sql语句查询操作之FromSql,ExecuteSqlCommand,SqlQuery
一.目前EF Core的版本为V2.1 相比较EF Core v1.0 目前已经增加了不少功能. EF Core除了常用的增删改模型操作,Sql语句在不少项目中是不能避免的. 在EF Core中上下文 ...
- EF Core中怎么实现自动更新实体的属性值到数据库
我们在开发系统的时候,经常会遇到这种需求数据库表中的行被更新时需要自动更新某些列. 数据库 比如下面的Person表有一列UpdateTime,这列数据要求在行被更新后自动更新为系统的当前时间. Pe ...
- [小技巧]EF Core中如何获取上下文中操作过的实体
原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...
- EF Core中避免贫血模型的三种行之有效的方法(翻译)
Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...
随机推荐
- java设计模式-观察者模式,装饰者模式
1.1定义 慨念:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. 即:主题和观察者定义了一对多的关系,观察者依赖于主题,只要主题发生变化,观察者就 ...
- 51NOD1847:奇怪的数学题
传送门 Sol 设 \(f(d)\) 表示 \(d\) 所有约数中第二大的,\(low_d\) 表示 \(d\) 的最小质因子 \[f(d)=\frac{d}{low_d}\] 那么 \[\sum_{ ...
- gulp & webpack整合
为什么需要前端工程化? 前端工程化的意义在于让前端这个行业由野蛮时代进化为正规军时代,近年来很多相关的工具和概念诞生.好奇心日报在进行前端工程化的过程中,主要的挑战在于解决如下问题:✦ 如何管理多个项 ...
- 如何使用火狐浏览器的Poster插件进行post请求
原文:http://blog.csdn.net/cjm2484836553/article/details/72453907 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ...
- Android架构设计之插件化、组件化
如今移动app市场已经是百花齐放,其中有不乏有很多大型公司.巨型公司都是通过app创业发展起来的:app类型更加丰富,有电子商务.有视频.有社交.有工具等等,基本上涵盖了各行各业每个角落,为了更加具有 ...
- 仿小米便签图文混排 EditText解决尾部插入文字bug
一直想实现像小米便签那样的图文混排效果,收集网上的办法无非三种: 1.自定义布局,每张图片是一个ImageView,插入图片后插入EditText,缺点是实现复杂,不能像小米便签那样同时选中图片和文字 ...
- Java快速入门-02-基础篇
Java快速入门-02-基础篇 上一篇应该已经让0基础的人对 Java 有了一些了解,接一篇更进一步 使用 Eclipse 快捷键 这个老师一般都经常提,但是自己不容易记住,慢慢熟练 快捷键 快捷键作 ...
- 第2课 android机器人的翻页相册
一.准备:准备像素大小相同的图片若干张.(本例中的图片,统一像素大小为310*310,请保存至本机电脑并按顺序命名为1.jpg至6.jpg) 二.启动与登陆: 启动谷歌浏览器,并在地址栏里输入loca ...
- Android内存管理-OnTrimMemory
Application中有两个与内存管理相关的方法:onLowMemory()和 onTrimMemory(int level),源码如下 @CallSuper public void onLowMe ...
- Java笔记-IO流的运用
--如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3877386.html "谢谢-- 1.InputStream和System ...