如何在实体框架中读取和写入多对多关联
本文关键字:关联 读取 实体 框架 | 更新日期: 2023-09-27 18:30:17
这似乎应该很简单,但多年来我一直无法找到清晰/好的解决方案。
很简单,我正在使用实体框架构建一个聊天程序来存储我的用户数据。
我有一个名为User的类。用户有一个朋友(其他用户)列表,如下所示
public class User
{
[Key]
public int ID { get; set; }
//whole bunch of other properties like 'username' etc.
public List<User> Friends { get; set; }
public User()
{
this.Friends = new List<User>();
}
}
EF 非常高兴地创建了我的用户表,但我一生都无法弄清楚如何编写/读取用户列表。
在寻找答案时,我确实发现了一件事:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
HasMany(m => m.Friends).
WithMany()
.Map(m =>
{
m.ToTable("UsersFriends");
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
});
}
这为我创建了一个很好的表格,其中包含用户的 ID 和用户朋友的 ID,但不知道如何将列表添加到此表。我猜是因为新列表()与DBContext无关。
你应该把你的朋友列表virtual
:
public virtual ICollection<User> Friends { get; set; }
我不太确定是否需要使用 ICollection<T>
而不是 List<T>
,但这是我在大多数 EF 示例中看到的,也是我自己多年来一直在使用的。
要添加好友,您可以执行以下操作:
var john = new User { Username = "John" };
var bob = new User { Username = "Bob" };
john.Friends.Add(bob);
context.Users.Add(john);
context.Users.Add(bob);
context.SaveChanges();
要获取用户的好友列表,您可以执行以下操作:
// Eager load the friends
var userWithFriends = context.Users.Include(x => x.Friends).SingleOrDefault(x => x.Username == "john");
// Lazy loading
var user = context.Users.SingleOrDefault(x => x.Username == "John");
var friends = user.Friends.ToList();
更新
向现有用户添加新好友
var dave = new User { Username = "Dave" };
var john = context.Users.Single(x => x.Username == "John");
john.Friends.Add(dave);
context.SaveChanges();
将现有用户作为好友添加到现有用户
var john = context.Users.Single(x => x.Username == "John");
var walter = context.Users.Single(x => x.Username == "Walter");
john.Friends.Add(walter);
context.SaveChanges();
// OR
var bob = new User { UserId = 2 };
var walter = new User { UserId = 4 };
context.Users.Attach(bob);
context.Users.Attach(walter);
bob.Friends.Add(walter);
context.SaveChanges();
移除好友
var john = context.Users.Single(x => x.Username == "John");
var bob = context.Users.Single(x => x.Username == "Bob");
john.Friends.Remove(bob);
context.SaveChanges();
// OR
var john = context.Users.Single(x => x.Username == "John");
var walter = new User { UserId = 4 };
context.Users.Attach(walter);
john.Friends.Remove(walter);
context.SaveChanges();
感谢克里斯托夫和其他人,我设法解决了我的问题,并实现了创建/保存/更新list<t>
到我的数据库的能力。这是我所做的:
用户类:
public class User
{
[Key]
public int ID { get; set; }
//whole bunch of other properties like 'username' etc.
//I have switched back to List<T> after using IEnumerable because despite what I keep reading
//you can use them, and I ran into problems using .ToList() where the results were empty.
public List<User> Friends { get; set; }
public User()
{
//Removed this line because the instance is created when we get the user using DbContext
//this.Friends = new List<User>();
}
}
创建关系表:
这将创建存储"朋友"列表内容的关系表。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
HasMany(m => m.Friends).
WithMany()
.Map(m =>
{
m.ToTable("UsersFriends");
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
});
}
从数据库获取列表
public static User GetUser(string Username)
{
using (Context DB = new Context())
{
//This creates the instance of List<User> and gets the data (again, thanks Kristof!)
return DB.Users.Include(x => x.Friends).SingleOrDefault(x => x.Username == Username);
}
}
将列表保存/更新到数据库
以前版本的代码使用 DB.Entry(user).State = EntityState.Modified; DB.SaveChanges();
可以很好地更新用户属性,但"朋友"列表被忽略,关系表没有更新,或者关系表已更新,但我最终在我的用户表中有重复的条目。这是因为用户实体在尝试更新时处于脱机状态。
解决此问题的解决方法是使用 GraphDiff 包,该包将扩展方法添加到 EF 以更新脱机实体。代码如下:
public static void UpdateUser(User user)
{
using (Context DB = new Context())
{
DB.UpdateGraph(user, map => map.AssociatedCollection(u => u.Friends));
DB.SaveChanges();
}
}
可能有一个更复杂的答案,但我是 EF 的新手,这对我有用。