.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();
}