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时填充 Children 和 Parent,但不会自动创建外键。
方法 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();//结束加锁事务
浙公网安备 33010602011771号