.net core 需要帮助来更新模型吗

.net core 需要帮助来更新模型吗,.net-core,.net Core,我有一个名为俱乐部应用程序用户的模型,它是俱乐部和应用程序用户之间的桥梁,是身份用户模型的扩展模型: public class ClubApplicationUser { public Guid ClubID { get; set; } public Club Club { get; set; } public string Id { get; set; } public ApplicationUser ApplicationUser { get; set;

我有一个名为俱乐部应用程序用户的模型,它是俱乐部应用程序用户之间的桥梁,是身份用户模型的扩展模型:

public class ClubApplicationUser
{
    public Guid ClubID { get; set; }

    public Club Club { get; set; }

    public string Id { get; set; }

    public ApplicationUser ApplicationUser { get; set; }

    public DateTime DateCreated { get; set; }

    public string CreatedBy { get; set; }

    public DateTime LastDateModified { get; set; }

    public string LastModifiedBy { get; set; }

    public DateTime? DateDeleted { get; set; }

    public string DeletedBy { get; set; }

    public bool IsDeleted { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }

    [ForeignKey("CreatedBy")]
    public ApplicationUser ClubApplicationCreatedUser { get; set; }

    [ForeignKey("LastModifiedBy")]
    public ApplicationUser ClubApplicationLastModifiedUser { get; set; }



}
在ApplicationDBContext-OnModelCreating中,我们定义了以下关系:

builder.Entity<ClubApplicationUser>()
                .HasKey(bc => new { bc.ClubID, bc.Id });

        builder.Entity<ClubApplicationUser>()
                .HasOne(bc => bc.Club)
                .WithMany(b => b.ClubApplicationUsers)
                .HasForeignKey(bc => bc.ClubID);

        builder.Entity<ClubApplicationUser>()
                .HasOne(bc => bc.ApplicationUser)
                .WithMany(c => c.ClubApplicationUsers)
                .HasForeignKey(bc => bc.Id);

我们可以更新这个没有问题。因此,它看起来像是.Net核心模型中的矛盾。我认为最好的解决方案是您需要删除和插入,而不是更新,尽管考虑到您的ClubApplication用户,实际上这可能意味着IsDeleted字段的更新,而不是实际执行删除操作

如果你从你所在领域的逻辑来考虑,我不认为一个用户通常会改变成为一个俱乐部的成员,而是离开(删除)一个俱乐部,加入(插入)另一个俱乐部

虽然我可以提出另一个域,在这个域中进行更新是有意义的,所以我认为这不是一个好的通用参数

下面的代码显示了问题的简化版本。您可以看到,测试允许插入和删除,但更新失败

public class Club
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<ClubUser> Users { get; set; }
}

public class User
{
    public int Id { get; set; }

    public string Name { get; set; }

    public IList<ClubUser> Clubs { get; set; }
}

public class ClubUser
{
    public int ClubID { get; set; }

    public Club Club { get; set; }

    public int Id { get; set; }

    public User User { get; set; }

    public string Extra { get; set; }
}

public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Club> Clubs { get; set; }
    public DbSet<ClubUser> ClubUsers { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
    }

    protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
    {
        builder.Entity<Club>()
            .HasKey(c => c.Id );

        builder.Entity<User>()
            .HasKey(c => c.Id );

        builder.Entity<ClubUser>()
            .HasKey(cu => new { cu.ClubID, cu.Id });

        builder.Entity<ClubUser>()
                .HasOne<Club>(cu => cu.Club)
                .WithMany(u => u.Users)
                .HasForeignKey(bc => bc.ClubID);

        builder.Entity<ClubUser>()
                .HasOne<User>(cu => cu.User)
                .WithMany(c => c.Clubs)
                .HasForeignKey(cu => cu.Id);

    }
}

[TestClass]
public class ManyToMany
{
    [TestMethod]
    public void DeleteAndInsert()
    {
        var context = new ApplicationDbContext();

        var clubusers = context.ClubUsers;
        var clubs = context.Clubs;
        var users = context.Users;

        var original = clubusers.First();
        clubusers.Remove(original);

        var newClubUser = new ClubUser
        {
            Club = clubs.Last(),
            User = users.First(),
            Extra = "Another"
        };

        clubusers.Add(newClubUser);

        context.SaveChanges();
    }

    [TestMethod]
    public void Update()
    {
        var context = new ApplicationDbContext();

        var clubusers = context.ClubUsers;
        var clubs = context.Clubs;
        var users = context.Users;

        var update = clubusers.First();
        update.Club = clubs.Last();
        update.Extra = "Changed";

        Assert.ThrowsException<InvalidOperationException>( () =>  context.SaveChanges());
    }
}

如果您可以控制数据库模式,另一个解决方案是向链接表添加一个surragate键


然后,如果您需要更新俱乐部或用户,您不会更改实体唯一标识符,因此将被允许使用。

*这不起作用-请参阅注释*

我的第三个选择是最快实现的,但是我不确定所有的影响

如果将OnModelCreating更改为设置索引而不是键

i、 e

builder.Entity()
.HasKey(cu=>new{cu.ClubID,cu.Id});
变成

        builder.Entity<ClubUser>()
            .HasIndex(cu => new { cu.ClubID, cu.Id });
builder.Entity()
.HasIndex(cu=>new{cu.ClubID,cu.Id});
更新现在可以工作了,但是您没有ClubUser上的密钥,这可能会导致其他问题。

关于:

InvalidOperationException:实体类型上的属性“ClubID” “ClubaApplicationUser”是密钥的一部分

ClubApplicationUsers表中的PrimaryKey是ClubID和Id。 您不能仅按Id对现有记录进行更改

例如:

var clubApplicationUserToUpdate = await _context.ClubApplicationUser
    .FirstOrDefaultAsync(m => m.Id == id.ToString());
必须是这样的:

var clubApplicationUserToUpdate = await _context.ClubApplicationUser
    .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
或:

关于:

第二种方法:

结果:违反主键约束“PK_clubaplicationuser”

我将举例说明:

Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
试图删除A1,并添加A2-它说A2已经存在

解决方案更接近第二种方法:

public async Task<IActionResult> OnPostAsync(Guid id)
{
    if (!this.ModelState.IsValid)
    {
        return Page();
    }

    //delete all club memberships and add new one
    var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
        .Where(m => m.Id == id.ToString()).ToList();

    foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
    {
        _context.ClubApplicationUser.Remove(clubApplicatonUser);
    }

    _context.ClubApplicationUser.Add(new ClubApplicationUser()
    {
        Id = id.ToString(),
        ClubID = AssignClubUser.SelectedClubID
    });

    await _context.SaveChangesAsync();

    return Page();
}
公共异步任务OnPostAsync(Guid id) { 如果(!this.ModelState.IsValid) { 返回页(); } //删除所有俱乐部会员并添加新会员 var clubaplicationuserstoremove=wait\u context.clubaplicationuser 。其中(m=>m.Id==Id.ToString()).ToList(); foreach(clubaplicationuserstoremove中的变量clubaplicationuser) { _context.clubaplicationuser.Remove(clubaplicationuser); } _context.ClubApplicationUser.Add(新ClubApplicationUser()) { Id=Id.ToString(), clubiser=AssignClubUser.selectedclubiser }); wait_context.SaveChangesAsync(); 返回页(); } 如果不想删除任何内容,只需添加新记录:

public async Task<IActionResult> OnPostAsync(Guid id)
{
    if (!this.ModelState.IsValid)
    {
        return Page();
    }

    // dont delete, just add new one
    var clubApplicatonUserExists = await _context.ClubApplicationUser
        .Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();


    if (clubApplicatonUserExists == null)
    {
        _context.ClubApplicationUser.Add(new ClubApplicationUser()
        {
            Id = id.ToString(),
            ClubID = AssignClubUser.SelectedClubID
        });

        await _context.SaveChangesAsync();
    }

    return Page();
}
公共异步任务OnPostAsync(Guid id) { 如果(!this.ModelState.IsValid) { 返回页(); } //不要删除,只添加一个新的 var clubaplicationuserexists=wait\u context.clubaplicationuser 其中(m=>m.Id==Id.ToString()&&m.ClubID==AssignClubUser.SelectedClubID).FirstOrDefaultAsync(); if(clubaplicationuserexists==null) { _context.ClubApplicationUser.Add(新ClubApplicationUser()) { Id=Id.ToString(), clubiser=AssignClubUser.selectedclubiser }); wait_context.SaveChangesAsync(); } 返回页(); }
As-on-domain使用软删除方法。。我们无法执行硬删除然后创建的方法。这很有趣。。。如果我们在数据库级别上这样做(参见UPDATE-1),它工作得很好,但在代码级别上没有。当我提到“实际上它可能意味着IsDeleted字段的更新,而不是实际执行删除”时,我就怀疑这一点。在这种情况下,您可以更改原始文件以将其标记为已删除(设置IsDeleted标志),并创建一个从原始文件克隆的新文件。这是否满足您的域要求,或者您必须更新现有记录?澄清为什么您可以在数据库中而不是在EntityFramework中这样做。实体框架需要标识一个实体,它使用键来标识实体,这样它就可以跟踪各种事情,例如更改—SQL UPDATE语句需要WHERE子句中的键属性。因此,键属性构成了实体的标识,如果您更改它们,则会出现问题。你可以把它看作是一个不同的实体,因此我提出了创建一个新实体的方法。如果你看看SetPropertyModified方法中的EntityFrameworkCore,你可以看到它有显式的代码阻止对键的更新,并将InvalidOperationException抛到我的脑海中,这是as ClubApplication用户不是“实体”-它并不真正负责自己的生命周期,如果您使用的是另一个更以对象为中心的持久性存储,它应该是俱乐部和/或用户的一部分。我现在更了解这一点,不设置密钥,它现在隐式地将Id字段作为密钥(即用户Id)由于属性的名称-如果改为调用UserId,则在实例化上下文时会出现一个错误,即没有主键。这意味着您无法在更改Id属性时更改用户,只有俱乐部,如果你有一个拥有多个俱乐部的用户,它将更新数据库中的所有俱乐部,这是不好的。使用这种方法-“不想删除任何内容,只需添加新记录”。。。我们如何区分这一点
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
    .FirstOrDefaultAsync(m => m.Id == id.ToString());
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
    .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
        .Where(m => m.Id == id.ToString()).ToList();
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
public async Task<IActionResult> OnPostAsync(Guid id)
{
    if (!this.ModelState.IsValid)
    {
        return Page();
    }

    //delete all club memberships and add new one
    var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
        .Where(m => m.Id == id.ToString()).ToList();

    foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
    {
        _context.ClubApplicationUser.Remove(clubApplicatonUser);
    }

    _context.ClubApplicationUser.Add(new ClubApplicationUser()
    {
        Id = id.ToString(),
        ClubID = AssignClubUser.SelectedClubID
    });

    await _context.SaveChangesAsync();

    return Page();
}
public async Task<IActionResult> OnPostAsync(Guid id)
{
    if (!this.ModelState.IsValid)
    {
        return Page();
    }

    // dont delete, just add new one
    var clubApplicatonUserExists = await _context.ClubApplicationUser
        .Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();


    if (clubApplicatonUserExists == null)
    {
        _context.ClubApplicationUser.Add(new ClubApplicationUser()
        {
            Id = id.ToString(),
            ClubID = AssignClubUser.SelectedClubID
        });

        await _context.SaveChangesAsync();
    }

    return Page();
}