簡體   English   中英

實體框架更新多對多關系:虛擬與否

[英]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集合被聲明為virtualuser.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM