以多对多关系保存数据
本文关键字:数据 保存 关系 | 更新日期: 2023-09-27 18:07:28
我有三个表,我正在使用User, Application, ApplicationAdministrator。ApplicationAdministrator是一个映射表,用于将User链接到具有多对多关系的Application。当我尝试保存一个新应用程序并将用户添加为管理员时,我得到以下错误:
不能定义两个对象之间的关系,因为它们附加到不同的ObjectContext对象。
所以我的下一步是创建一个BaseRepository,它有一个公共上下文可以从中提取。但是,现在当我尝试修改已经附加到上下文的实体时,我得到以下错误:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
为什么这是一个如此困难的过程?我已经看到了解决方案,连接,重新连接和分离,并在你的头上旋转5次,然后一切都会工作。将实体附加到一个上下文最终会导致实体的重复,这取决于我将其附加到哪个上下文。
非常感谢所有的帮助!
UserRepository.cs:
public class UserRepository : BaseRepository<User>, IUserRepository
{
// private ManagerDbContext _context = new ManagerDbContext();
public UserRepository(ManagerDbContext context)
: base(context) { }
public IQueryable<User> Users
{
get { return _context.Users.Include("Administrates").Include("Company"); }
}
public void SaveUser(User user)
{
_context.Entry(user).State = user.Id == 0 ? EntityState.Added : EntityState.Modified;
_context.SaveChanges();
}
public void DeleteUser(User user)
{
_context.Users.Remove(user);
_context.SaveChanges();
}
}
ApplicationRepository.cs:
public class ApplicationRepository : BaseRepository<Application>, IApplicationRepository
{
// private ManagerDbContext _context = new ManagerDbContext();
public ApplicationRepository(ManagerDbContext context)
: base(context) { }
public IQueryable<Application> Applications
{
get { return _context.Applications.Include("Administrators"); }
}
public void SaveApplication(Application app)
{
_context.Entry(app).State = app.Id == 0 ? EntityState.Added : EntityState.Modified;
_context.SaveChanges();
}
public void DeleteApplication(Application app)
{
_context.Applications.Remove(app);
_context.SaveChanges();
}
}
UserConfiguration.cs:
public UserConfiguration()
{
this.HasKey(x => x.Id);
this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.FirstName).IsRequired();
this.Property(x => x.LastName).IsRequired();
this.Property(x => x.Username).IsRequired();
this.Property(x => x.CompanyId).IsRequired();
this.HasRequired(user => user.Company).WithMany().HasForeignKey(user => user.CompanyId);
this.HasRequired(user => user.Company).WithMany(company => company.Users).WillCascadeOnDelete(false);
this.HasMany(user => user.Administrates)
.WithMany(application => application.Administrators)
.Map(map => map.MapLeftKey("UserId")
.MapRightKey("ApplicationId")
.ToTable("ApplicationAdministrators"));
}
ApplicationConfiguration.cs:
public ApplicationConfiguration()
{
this.HasKey(x => x.Id);
this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.Name).IsRequired();
this.Property(x => x.Description);
this.HasMany(application => application.Administrators)
.WithMany(user => user.Administrates)
.Map(map => map.MapLeftKey("ApplicationId")
.MapRightKey("UserId")
.ToTable("ApplicationAdministrators"));
}
保存实体的代码段。
long appId = Int64.Parse(form["ApplicationId"]);
long userId = Int64.Parse(form["UserId"]);
Application app = appRepository.Applications.FirstOrDefault(a => a.Id == appId);
User user = userRepository.Users.FirstOrDefault(u => u.Id == userId);
app.Administrators.Add(user);
appRepository.SaveApplication(app);
return RedirectToAction("Index");
如果您使用两个不同的上下文,您必须从第一个上下文分离实体,并将其附加到您想要执行更改的第二个上下文。您还必须正确配置其状态。要在DbContext API中分离实体,你需要调用:
context.Entry(entity).State = EntityState.Detached;
如果你使用相同的上下文来加载所有实体并保存它们的更改,你不需要更改它们的状态。它是通过变更跟踪自动完成的。
顺便说一句。你应该从没有资源库开始,一旦你了解了EF是如何工作的,以及资源库是如何帮助你的,你就应该开始使用资源库。
这是我最终想到的解决方案。
我创建了一个Func字典,以便在我的上下文中将实体附加到正确的EntitySet。一个缺点是您必须硬编码EntitySet名称,所以我在POCO中的一个静态变量中这样做。
BaseRepository.cs
public class BaseRepository<T> where T : class
{
public static ManagerDbContext baseContext;
public BaseRepository() { }
public BaseRepository(ManagerDbContext context)
{
baseContext = context;
}
private static object _entity;
public void AttachEntity(object entity)
{
_entity = entity;
entityAttachFunctions[entity.GetType().BaseType]();
}
private Dictionary<Type, Func<bool>> entityAttachFunctions = new Dictionary<Type, Func<bool>>()
{
{typeof(User), () => AttachUser()},
{typeof(Application), () => AttachApplication()}
};
private static bool AttachUser()
{
((IObjectContextAdapter)baseContext).ObjectContext.AttachTo(User.TableName, _entity);
return true;
}
private static bool AttachApplication()
{
((IObjectContextAdapter)baseContext).ObjectContext.AttachTo(Application.TableName, _entity);
return true;
}
}
UserRepository.cs
public void AttachEntity(object entity)
{
baseContext = _context;
base.AttachEntity(entity);
}
public void DetachUser(User user)
{
_context.Entry(user).State = EntityState.Detached;
_context.SaveChanges();
}
ApplicationRepository.cs
public void AttachEntity(object entity)
{
baseContext = _context;
base.AttachEntity(entity);
}
public void DetachApplication(Application app)
{
_context.Entry(app).State = EntityState.Detached;
_context.SaveChanges();
}
AdminController.cs
long appId = Int64.Parse(form["ApplicationId"]);
long userId = Int64.Parse(form["UserId"]);
Application app = appRepository.Applications.FirstOrDefault(a => a.Id == appId);
User user = userRepository.Users.FirstOrDefault(u => u.Id == userId);
userRepository.DetachUser(user);
appRepository.AttachEntity(user);
app.Administrators.Add(user);
appRepository.SaveApplication(app);