我正在使用帶有EF核心的ASP.NET核心。我的情況如下:

在此處輸入圖像描述

我有管理,我可以編輯特定組。當我想將用戶添加到組時,我必須通過<select>來執行此操作,我在其中加載所有用戶。例如:

<select id="Users" multiple="multiple" name="Users">
    <option selected="selected" value="1">example.user</option>
    <option value="5">sharp.john</option>
    <option value="6">bruce.lee</option>
</select>

所以這很好。當我提交表單時,我得到用戶ID的List<int>

比我創建完整的組(因為我也在表單中獲取其他數據):

// Create list of GroupUser
var groupUsers = new List<GroupUser>();

foreach (var userId in groupsEditViewModel.Users)
{
    // Create GroupUser
    var groupUser = new GroupUser
    {
        UserId = userId
    };

    // Add it to the list of GroupUser
    groupUsers.Add(groupUser);
}

// Create group from viewmodel
var group = new Group
{
    Name = name,
    Description = groupsEditViewModel.Description,
    Priority = groupsEditViewModel.Priority,
    GroupUsers = groupUsers
};

仍然一切正常,但是當我調用我的存儲庫來更新這個值時,它變得很瘋狂,只是因為更新了GroupUsers。

我想用這個:

public void EditGroup(Group group)
{
    // Get group from db
    var groupToEdit = _authDbContext.Groups
        .SingleOrDefault(g => g.Name == group.Name);

    // Add GroupId to GroupUsers
    foreach (var groupUser in group.GroupUsers)
    {
        groupUser.GroupId = groupToEdit.GroupId;
    }

    // Update GroupUsers
    groupToEdit.GroupUsers = group.GroupUsers;

    // Save to the db
    _authDbContext.SaveChanges();
}

但它以錯誤結束:

無法跟踪實體類型“GroupUser”的實例,因為已經跟踪了具有相同密鑰的此類型的另一個實例。添加新實體時,對於大多數鍵類型,如果未設置任何鍵,則將創建唯一的臨時鍵值(即,如果為鍵屬性指定了其類型的默認值)。如果要為新實體顯式設置鍵值,請確保它們不會與現有實體或為其他新實體生成的臨時值發生衝突。附加現有實體時,請確保只有一個具有給定鍵值的實體實例附加到上下文。

所以我最終做了一些有用的事情。像這樣的東西:

public void EditGroup(Group group)
{
    // Get group from db
    var groupToEdit = _authDbContext.Groups
        .SingleOrDefault(g => g.Name == group.Name);

    // Add GroupId to GroupUsers
    foreach (var groupUser in group.GroupUsers)
    {
        groupUser.GroupId = groupToEdit.GroupId;
    }

    // Copy to edit
    var groupUserToEditCopy = new List<GroupUser>(groupToEdit.GroupUsers);

    // List to remove
    foreach (var groupUserToEdit in groupToEdit.GroupUsers)
    {
        // Load UserId and GroupId
        var userId = groupUserToEdit.UserId;
        var groupId = groupUserToEdit.GroupId;

        // Check if there is still this groupUser
        if (group.GroupUsers.Count(g => g.GroupId == groupId && g.UserId == userId) == 0)
        {
            // If not, than remove this groupUser
            groupUserToEditCopy.RemoveAll(g => g.GroupId == groupId && g.UserId == userId);
        }
    }

    // List to add
    foreach (var groupUser in group.GroupUsers)
    {
        // Load UserId and GroupId
        var userId = groupUser.UserId;
        var groupId = groupUser.GroupId;

        // Check if there is new groupUser
        if (groupToEdit.GroupUsers.Count(g => g.GroupId == groupId && g.UserId == userId) == 0)
        {
            // If not, than add this groupUser
            groupUserToEditCopy.Add(groupUser);
        }
    }

    // Modified copy to original
    groupToEdit.GroupUsers = groupUserToEditCopy;

    // Save to the db
    _authDbContext.SaveChanges();
}

我認為,這個解決方案不是最優的,必須有辦法更容易地做到這一點。你有解決方案嗎?

一般承認的答案

我可以在這里和你的課程一起回答。但是,這確實要求所有用戶和組都已在數據庫中。

然後使用此擴展我刪除未選中並將新選擇添加到列表中

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
    {
        db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
        db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
    }

    public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
    {
        return items
            .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
            .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
            .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
            .Select(t => t.t.item);
    }

使用它看起來像這樣

 var model = db.Groups
             .Include(x => x.GroupUser)
             .FirstOrDefault(x => x.GroupId == group.GroupId);

 db.TryUpdateManyToMany(model.GroupUser, listOfNewUsers
 .Select(x => new GroupUser
 {
     UserId = x,
     GroupId = group.GroupId
 }), x => x.UserId );


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow