一、 EF中每次查询都通过数据库,而不通过DBContext缓存的方法:
在Entity Framework (EF) 中,默认情况下,当你使用 “db.Set<T>().Find(id)【返回实体】” 或 “db.Set<T>().Where(condition)【返回IQueryable<T>,再FirstOrDefault()获取实体】” 方法查询实体时,如果DBContext上下文中已经存在相应的实体,它将返回缓存中的实体,而不会去数据库中查询。但如果你需要确保每次都从数据库中查询,不使用缓存,你可以使用 AsNoTracking 查询
using (var context = new YourDbContext())
{
var entity = context.Set<YourEntity>()
.AsNoTracking()
.Find(id);
// 这里的entity是从数据库中查询的,不会跟踪变化
}
二、 EF更新语句“只更新部分字段”和“更新所有字段”的可采用的方法:
假如说有个实体UserInfo ,其中的UserId是主键,定义个实体如下
UserInfo userInfonew = new UserInfo()
{
UserId=userInfo.UserId,
Email = userInfo.Email,
FirstName = userInfo.FirstName,
LastName = userInfo.LastName,
LastUpdateBy = GetCurrentUserGuid(),
LastUpdate = DateTime.Now
};
1、如果全部字段需要更新可以使用如下方法:
db.Entry(userInfonew).State = EntityState.Modified;
db.SaveChanges();
注意:
如上直接通过“db.Entry(userInfonew).State = EntityState.Modified”设置实体的State, 将会使实体的所有字段Property的IsModified都为True(这大概也解释了为什么直接设置实体State属性为Modifid会导致最终的SQL会是更新全部的字段);
而如果只是通过 “实体对象.字段=值”(如“userInfonew.Email="abc@qq.com"”)更新实体字段值,那么虽然也会使得db.Entry(userInfonew).State 变更为 EntityState.Modified,但这时实体的字段中只有Email字段状态为被修改,即:db.Entry(userInfonew).Property(x => x.Email).IsModified为true, 而其余字段的状态依然为未被修改的状态,比如:db.Entry(userInfonew).Property(x => x.FirstName).IsModified这时依然为false, 而在这种通过更改部分字段值导致读取 db.Entry(userInfonew).State已经是 EntityState.Modified的情况下,再次用 “db.Entry(userInfonew).State = EntityState.Modified”赋值,虽然实体的State本来就是EntityState.Modified赋值没变,但这时已经把所有字段的IsModified属性都设置成true了,即会造成: “db.Entry(userInfonew).Property(x=>x.FirstName).IsModified也变成了true【其余字段也同样】”
2、假如我们想更新部分字段,如只想更新Email的信可以采用以下方法:
db.UserInfoes.Attach(userInfonew);
db.Entry(userInfonew).Property(x => x.Email).IsModified = true;db.SaveChanges();
三、将实体所有字段都恢复到未修改前的值,即:第一次加载从数据库获取的初始值或执行Attach时值:
方法:db.Entry(userInfonew).State = EntityState.Unchanged;
设置实体State为Unchanged 不仅是改变了实体状态,还会将实体全部字段值都恢复到“刚从DB读取到的值” 或者 “执行完Attach实体时对应实体的字段值” (下文转载中也有对该特点的详细案例)
(以上总结都是经过实验验证过的)
下文转载自: https://www.cnblogs.com/OpenCoder/p/9839588.html
EF Core 中多次从DBContext查询实体数据,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(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
Co

3007

被折叠的 条评论
为什么被折叠?



