惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
量子位
月光博客
月光博客
罗磊的独立博客
宝玉的分享
宝玉的分享
博客园_首页
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
WordPress大学
WordPress大学
博客园 - 叶小钗
博客园 - 聂微东
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
雷峰网
雷峰网
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - Franky
美团技术团队
爱范儿
爱范儿
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Y
Y Combinator Blog

博客园 - 火冰·瓶

Cloudflare Tunnel 有两种模式 用 Cloudflare Tunnel 实现从外网 SSH 登录内网服务器 Cloudflare + .NET Core MVC 部署完整流程 net core 后端获取网络图片的扩展名 推荐好用的web控件 ubuntu运维 使用Frp+Caddy把https映射到内网的web服务 net core中结合EntityFrameworkCore的DB first使用PostgreSQL OneDrive中设置不同步某个文件夹 Framework 4.7老项目运行到caddy Action<T> 和 Func<T>的用法 net core随记 asp.net core 记录页面访问次数,同一个IP一个小时只算一次 asp.net core 记录页面访问记录 asp.net core 发布到caddy asp.net core发布到Caddy获取用户的真实ip 统计文章阅读量,一小时内重复刷新的不重复统计 使用on()方法绑定事件,解决动态加载的元素事件绑定 鼠标悬停在图片上方时,显示文字
EF Core经验
火冰·瓶 · 2025-04-15 · via 博客园 - 火冰·瓶

1.使用了db first 后,增加额外的显式配置外键而不影响自动生成的配置

加入有2个表,分别是TaskInfo(任务表),和TaskInfoSubmitResumeRecord(任务提交记录表)

TaskInfo和TaskInfoSubmitResumeRecord是一对多的关系

在TaskInfoSubmitResumeRecord中有一个字段TaskId指向TaskInfo的主键(数据库中不用设置外键,设置了会自动生成导航属性和配置)

需要建3个部分类分别为:

public partial class TaskInfo
{
  // 导航属性
  public virtual ICollection<TaskInfoSubmitResumeRecord>? TaskInfoSubmitResumeRecords { get; set; }
}



public partial class TaskInfoSubmitResumeRecord
{
  //导航属性
  public virtual TaskInfo? TaskInfo { get; set; }
}



public partial class MySqlTrainDbContext
{
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TaskInfoSubmitResumeRecord>()
                .HasOne(c => c.TaskInfo)
                .WithMany(p => p.TaskInfoSubmitResumeRecords)
                .HasForeignKey(c => c.TaskId);
    }
}

Copilot中的建议

EF Core 中,即使没有外键,你仍然可以定义 导航属性 来建立实体之间的关系。通常有以下几种方法:

方法 1:使用 virtual 导航属性

即使没有外键,你仍然可以在实体类中定义导航属性:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }

    // 导航属性
    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }

    // 导航属性
    public virtual Parent Parent { get; set; }
}

这样,EF Core 仍然可以在查询Includ时填充 ChildrenParent,但不会自动创建外键。  

方法 2:使用 Fluent API 显式配置关系

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Child>()
        .HasOne(c => c.Parent)
        .WithMany(p => p.Children)
        .HasForeignKey("ParentId") // 这里不定义外键字段
        .IsRequired(false); // 允许为空
}

方法 3:使用 Shadow Property

如果你不想在实体类中定义外键字段,可以使用 Shadow Property

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Child>()
        .Property<int>("ParentId"); // 影子属性
}

2.将Lambda表达式作为参数传递给方法

EF Core 中,你可以将 Lambda 表达式 作为参数传递给方法,以便动态构建查询。通常有以下几种方式:

1. 直接传递 Lambda 表达式

你可以创建一个方法,接受 Expression<Func<T, bool>> 作为参数:

public List<User> GetUsers(Expression<Func<User, bool>> predicate)
{
    using (var context = new MyDbContext())
    {
        return context.Users.Where(predicate).ToList();
    }
}



//然后调用
var users = GetUsers(u => u.Age > 18);

2. 使用泛型方法

如果你希望方法更加通用,可以使用泛型:

public List<T> GetEntities<T>(Expression<Func<T, bool>> predicate) where T : class
{
    using (var context = new MyDbContext())
    {
        return context.Set<T>().Where(predicate).ToList();
    }
}


//调用
var users = GetEntities<User>(u => u.Age > 18);

3. 结合 AsQueryable

如果你希望在不同的查询条件下复用 Lambda 表达式,可以使用 AsQueryable

Expression<Func<User, bool>> filter = u => u.Age > 18;
var users = context.Users.AsQueryable().Where(filter).ToList();

3.根据动态字段进行排序

string orderColumn = "Name"; // 通过外部传入字段名
query = query.OrderByDescending(x => EF.Property<object>(x, orderColumn));

如果要动态对父对象的字段名进行排序,使用System.Linq.Dynamic.Core

dotnet add package System.Linq.Dynamic.Core
using System.Linq.Dynamic.Core;

query = query.OrderBy("AddTime ascending"); //升序
query = query.OrderBy("AddTime descending");//降序

query = query.OrderBy("Account.AddTime ascending"); //升序   升序ascending可以省略
query = query.OrderBy("Account.AddTime descending");//降序


query = query.OrderBy("AddTime");  //升序可以不加ascending

判断对象属性的属性中是否包含排序字段,即上述例子中,判断属性Account中是否包含AddTime

            //判断排序字段是否在对象属性中
            if (type.GetProperty("Account") != null && type.GetProperty("Account").PropertyType.GetProperty("AddTime") != null) 
            {
                if (orderDir == "asc")
                {
                    recordsQuery = recordsQuery.OrderBy("Account.AddTime");
                }
                else
                {
                    recordsQuery = recordsQuery.OrderBy("Account.AddTime descending");
                }
            }

4.使用悲观锁,适用于高并发写入,如订单管理、库存更新

using var context = new MyDbContext();
await using var transaction = await context.Database.BeginTransactionAsync(); //开始加锁事务
var user = await context.Users.FirstOrDefaultAsync();
user.Name = "Updated Name";// 进行数据修改
await context.SaveChangesAsync();
await transaction.CommitAsync();//结束加锁事务