[英]entity framework update many to many relationship: virtual or not
一年以來我一直在使用 EF4(不是代碼優先),所以我並不是真正的專家。 我對使用關於保存 n 更新的多對多關系有疑問。
我在 stackoverflow 上的某個地方讀到(我再也找不到 url 了),一種解決方案 - 更新現有的多對多關系 - 是不聲明“虛擬”屬性; 但是,如果我這樣做,引擎將無法輕松加載數據。
你能解釋一下原因嗎? Otherwire,你能幫我找到一些關於這個主題的很酷的文檔嗎?
謝謝
您可以通過這種方式更新多對多關系(例如,為用戶 3 提供角色 5):
using (var context = new MyObjectContext())
{
var user = context.Users.Single(u => u.UserId == 3);
var role = context.Roles.Single(r => r.RoleId == 5);
user.Roles.Add(role);
context.SaveChanges();
}
如果User.Roles
集合被聲明為virtual
行user.Roles.Add(role);
確實會觸發延遲加載,這意味着在添加新角色之前,首先從數據庫加載用戶的所有角色。
這實際上令人不安,因為您不需要加載整個Roles
集合來向用戶添加新角色。
但這並不意味着您必須刪除virtual
關鍵字並完全放棄延遲加載。 在這種特定情況下,您可以關閉延遲加載:
using (var context = new MyObjectContext())
{
context.ContextOptions.LazyLoadingEnabled = false;
var user = context.Users.Single(u => u.UserId == 3);
var role = context.Roles.Single(r => r.RoleId == 5);
user.Roles = new List<Role>(); // necessary, if you are using POCOs
user.Roles.Add(role);
context.SaveChanges();
}
編輯
如果您想更新用戶的整個角色集合,我更願意使用預先加載( = Include
)加載原始角色。 無論如何,您都需要此列表來刪除某些角色,因此您無需等到延遲加載從數據庫中獲取它們:
var newRolsIds = new List<int> { 1, 2, 5 };
using (var context = new MyObjectContext())
{
var user = context.Users.Include("Roles")
.Single(u => u.UserId == 3);
// loads user with roles, for example role 3 and 5
var newRoles = context.Roles
.Where(r => newRolsIds.Contains(r.RoleId))
.ToList();
user.Roles.Clear();
foreach (var newRole in newRoles)
user.Roles.Add(newRole);
context.SaveChanges();
}
除了從數據庫加載新角色之外,您還可以附加它們,因為您在示例中知道鍵屬性值。 您還可以完全刪除丟失的角色,而不是清除整個集合,而不是重新添加現有的角色:
var newRolsIds = new List<int> { 1, 2, 5 };
using (var context = new MyObjectContext())
{
var user = context.Users.Include("Roles")
.Single(u => u.UserId == 3);
// loads user with roles, for example role 3 and 5
foreach (var role in user.Roles.ToList())
{
// Remove the roles which are not in the list of new roles
if (!newRoleIds.Contains(role.RoleId))
user.Roles.Remove(role);
// Removes role 3 in the example
}
foreach (var newRoleId in newRoleIds)
{
// Add the roles which are not in the list of user's roles
if (!user.Roles.Any(r => r.RoleId == newRoleId))
{
var newRole = new Role { RoleId = newRoleId };
context.Roles.Attach(newRole);
user.Roles.Add(newRole);
}
// Adds roles 1 and 2 in the example
}
// The roles which the user was already in (role 5 in the example)
// have neither been removed nor added.
context.SaveChanges();
}
Slaumas 的回答非常好,但我想補充一下如何插入多對多關系而不先從數據庫加載對象。 如果您知道連接該額外數據庫調用的 ID,則是多余的。 關鍵是使用Attach()
。
關於附加的更多信息:
https://stackoverflow.com/a/3920217/3850405
public class ConnectBToADto
{
public Guid AId { get; set; }
public Guid BId { get; set; }
}
public void ConnectBToA(ConnectBToADto dto)
{
var b = new B() { Id = dto.BId };
Context.B.Attach(b);
//Add a new A if the relation does not exist. Redundant if you now that both AId and BId exists
var a = Context.A.SingleOrDefault(x => x.Id == dto.AId);
if(a == null)
{
a = new A() { Id = dto.AId };
Context.A.Add(a);
}
b.As.Add(a);
}
我正在使用 db-first 方法和 automapper 在模型和實體(MVC 5)之間進行映射,並使用預先加載。
在我的場景中,有設備並且可以有多個用戶作為設備操作員:
public void Create()
{
using (var context = new INOBASEEntities())
{
// first i need to map model 'came from the view' to entity
var _ent = (Equipment)Mapper.Map(this, typeof(EquipmentModel), typeof(Equipment));
context.Entry(_ent).State = System.Data.Entity.EntityState.Added;
// I use multiselect list on the view for operators, so i have just ids of users, i get the operator entity from user table and add them to equipment entity
foreach(var id in OperatorIds)
{
AspNetUsersExtended _usr = context.AspNetUsersExtended.Where(u => u.Id == id).FirstOrDefault();
// this is operator collection
_ent.AspNetUsersExtended2.Add(_usr);
}
context.SaveChanges();
Id = _ent.Id;
}
}
public void Update()
{
using (var context = new INOBASEEntities())
{
var _ent = (Equipment)Mapper.Map(this, typeof(EquipmentModel), typeof(Equipment));
context.Entry(_ent).State = System.Data.Entity.EntityState.Modified;
var parent = context.Equipment
.Include(x => x.AspNetUsersExtended2)//include operators
.Where(x => x.Id == Id).FirstOrDefault();
parent.AspNetUsersExtended2.Clear(); // get the parent and clear child collection
foreach (var id in OperatorIds)
{
AspNetUsersExtended _usr = context.AspNetUsersExtended.Where(u => u.Id == id).FirstOrDefault();
parent.AspNetUsersExtended2.Add(_usr);
}
// this line add operator list to parent entity, and also update equipment entity
context.SaveChanges();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.