EF Core 深入学习

EF Core操作实体属性的内部机制

  • 核心概念: EF Core 的直接字段访问

  • EF Core 在操作实体属性时,会尽量绕过属性的 getter/setter,直接操作背后的私有字段

    • 为什么要这么做?基于性能和对特殊功能支持的考虑
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class Person
    {
        public long Id { get; set; }
        // public string Name { get; set; } // 不用简写形式,更能验证结论
        private string _name; // 按照结论,会直接读_name,而不会先执行Name的get和set的逻辑
        public string Name
        {
            get {
                Console.WriteLine("get被调用了!");
                return _name;
            }
            set {
                Console.WriteLine("set被调用了!");
                _name = value;
            }
        }
    }
}

  • 数据库配置和主程序代码如下
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1
{
    public class MyDbContext : DbContext
    {
        public DbSet<Person> Persons { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            // 添加 TrustServerCertificate=true 解决证书问题
       optionsBuilder.UseSqlServer("Server=.;Database=ddd1;Trusted_Connection=True;TrustServerCertificate=true;");
        }
    }
}

// Program.cs
using ConsoleApp1;

Person p1 = new();
p1.Name = "yzk"; // 这里调用 setter,输出 "set被调用了!"
using var ctx = new MyDbContext();
ctx.Persons.Add(p1);
ctx.SaveChanges(); // 这里应该调用 getter,但实际没有!

  • 本次程序的运行结果
set被调用了!
  • 按照预想的结果,当执行完ctx.SaveChanges()后,控制台应该会打印get被调用了,然而并没有发生
    • 原因: EF Core 在保存时直接读取 _name 字段,没有通过 Name 属性的 getter。
using ConsoleApp1;

//Person p1 = new();
//p1.Name = "yzk";
//using var ctx = new MyDbContext();
//ctx.Persons.Add(p1);
//ctx.SaveChanges();

using var ctx = new MyDbContext();
Person p1 = ctx.Persons.First();
Console.WriteLine(p1.Id);
Console.WriteLine(p1.Name);  // 这里调用 getter

  • 程序运行结果
1
get被调用了!
yzk
  • 按理说,执行了两次Console,那么应该调用两次get,而事实上只调用了一次!
    • EF Core 在创建对象时直接设置 _name 字段,没有通过 setter
    • 只有当我们显式访问 p1.Name 时才调用 getter
  • 再次实验,当把字段从 _name 改为 xiaoming 后(主程序不变)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class Person
    {
        public long Id { get; set; }
       
        private string xiaoming;
        public string Name
        {
            get {
                Console.WriteLine("get被调用了!");
                return xiaoming;
            }
            set {
                Console.WriteLine("set被调用了!");
                xiaoming = value;
            }
        }
    }
}

  • 运行结果如下
set被调用了!      // 创建对象时通过 setter
get被调用了!      // EF Core 内部跟踪时调用 getter  
get被调用了!      // 再次内部调用
1
get被调用了!      // 我们显式访问时调用
yzk

原理分析

EF Core 的字段发现规则

EF Core 按照以下顺序寻找匹配的字段:

  1. _<propertyName>(如 _name
  2. _<PropertyName>(如 _Name
  3. <propertyName>(如 name
  4. <PropertyName>(如 Name

为什么会这样设计?

  1. 性能优化:直接访问字段比通过属性访问器更快
  2. 绕过业务逻辑:避免属性 setter 中的验证逻辑影响数据加载
  3. 跟踪变化:EF Core 需要直接监控字段值的变化

需要避免的情况

public string Name
{
    get { return _name; }
    set { 
        if(string.IsNullOrEmpty(value))
            throw new Exception("名称不能为空"); // EF Core 加载数据时会触发异常!
        _name = value; 
    }
}

推荐的实践

// 如果需要在 setter 中添加逻辑,要确保不影响 EF Core 的数据加载
public string Name
{
    get { return _name; }
    set { 
        // 避免在这里添加严格的验证逻辑
        _name = value; 
        // 或者添加不阻碍数据加载的逻辑
    }
}

总结

  • 数据加载时:EF Core 尽量直接操作字段,不触发属性逻辑
  • 显式访问时:才会调用我们定义的 getter/setter
  • 字段命名:会影响 EF Core 的行为方式

意义: 理解这个机制对于编写正确的 EF Core 实体类和避免潜在的 bug 非常重要!

posted @ 2025-11-27 13:36  清安宁  阅读(7)  评论(0)    收藏  举报