如何解决使用带有现有子实体的实体框架在数据库中创建实体的问题
我正在尝试使用 C# 中的实体框架在数据库中插入一个实体。这是在数据库中创建场地的函数。
public class Test {
public static void main(String[] args) {
Test example1 = new test();
Test example2 = example1;
Test example3=new test();
if (example1==example2){
System.out.println("1 and 2 are same");
}
if (example1==example3){
System.out.println("1 and 3 are same");
}
if (example2==example3){
System.out.println("2 and 3 are same");
}
if (example1.equals(example2)){
System.out.println("1 and 2 are equal");
}
if (example1.equals(example3)){
System.out.println("1 and 3 are equal");
}
if (example2.equals(example3)){
System.out.println("2 and 3 are equal");
}
}
}
同时插入可用性日期
public async Task<Guid> CreateVenue(CurrentUser currentUser,CreateVenueDTO data,IAuthenticator authenticator)
{
using (var db = contextFactory.GetContext())
{
var uid = Guid.NewGuid();
var venue = new Venue
{
Uid = uid,Name = data.Name,Address = data.Address,Description = data.Description,MaxCapacitySitting = data.MaxCapacitySitting,MaxCapacityStanding = data.MaxCapacityStanding,Lat = data.Lat,Lon = data.Lon
};
db.Venues.Add(venue);
await db.SaveChangesAsync();
venue.VenueFacilities = new List<VenueFacility>();
var venueFacilities = await db.Facilities.Where(x => data.FacilityUids.Contains(x.Uid)).ToListAsync();
venueFacilities.ForEach(facility => venue.VenueFacilities.Add(new VenueFacility { Facility = facility,FacilityId = facility.Id,Venue = venue,VenueId = venue.Id,Rank = 0 }));
db.Venues.Update(venue);
await db.SaveChangesAsync();
if (data.Organization != null)
{
var orgUid = data.Organization.Uid;
if (orgUid == Guid.Empty)
{
orgUid = await organizationService.CreateOrganization(currentUser,data.Organization);
}
venue.Organisation = db.Organizations.Where(x => x.Uid == orgUid).FirstOrDefault();
db.Venues.Update(venue);
await db.SaveChangesAsync();
}
var venueTypes = await db.Types.Where(x => data.TypeUids.Contains(x.Uid)).ToListAsync();
venue.VenueTypes = new List<VenueType>();
venueTypes.ForEach(type => venue.VenueTypes.Add(new VenueType { VenueId = venue.Id,TypeId = type.Id,Type = type }));
db.Venues.Update(venue);
await db.SaveChangesAsync();
venue.Pricing = new Pricing { Uid = Guid.NewGuid(),Package = data.Pricing.Package,MinimumHourCount = data.Pricing.MinimumHourCount ?? 0,MinimumPeopleCount = data.Pricing.MinimumPeopleCount ?? 0,WeekDays = data.Pricing.WeekDays ?? 0,WeekEnds = data.Pricing.WeekEnds ?? 0 };
db.Venues.Update(venue);
await db.SaveChangesAsync();
venue.VenueAvailabilityTimings = new List<AvailabilityTiming>();
data.AvailabilityTimings.ToList().ForEach(availability => {
DayTime from = null;
DayTime to = null;
WeekDay weekDay = db.WeekDays.Where(w => w.Id == availability.DayOfWeek).AsNoTracking().FirstOrDefault();
if (availability.From != null)
{
from = db.DayTimes.Where(f => f.Id == availability.From).AsNoTracking().FirstOrDefault();
db.Detatch<DayTime>(from);
}
if (availability.To != null)
{
to = db.DayTimes.Where(t => t.Id == availability.To).AsNoTracking().FirstOrDefault();
db.Detatch<DayTime>(to);
}
db.Detatch<WeekDay>(weekDay);
var newAvailability = new AvailabilityTiming
{
Uid = Guid.NewGuid(),From = from,To = to,DayOfWeek = weekDay,AvailableAllDay = availability.AvailableAllDay
};
venue.VenueAvailabilityTimings.Add(newAvailability);
});
db.Venues.Update(venue);
await db.SaveChangesAsync();
venue.HowFaradvanced = data.HowFaradvanced;
venue.VenueAvailabilityDates = new List<AvailabilityDate>();
data.AvailabilityDates.ToList().ForEach(availability =>
{
scalable_Web_Data.Models.Calendar from = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(f => f.Id == availability.From.Id).AsNoTracking().FirstOrDefault();
scalable_Web_Data.Models.Calendar to = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(t => t.Id == availability.To.Id).AsNoTracking().FirstOrDefault();
db.Detatch<scalable_Web_Data.Models.Calendar>(from);
db.Detatch<WeekDay>(db.WeekDays.Where(w => w.Id == from.DayOfWeek.Id).AsNoTracking().FirstOrDefault());
db.Detatch<scalable_Web_Data.Models.Calendar>(to);
db.Detatch<WeekDay>(db.WeekDays.Where(w => w.Id == to.DayOfWeek.Id).AsNoTracking().FirstOrDefault());
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,To = to
});
});
db.Venues.Update(venue);
await db.SaveChangesAsync();
return uid;
}
}
我收到此错误(即使我可能已分离所有子实体)
无法跟踪实体类型“WeekDay”的实例,因为已经跟踪了另一个具有相同 {'Id'} 键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。
这里是重要的类
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,To = to
});
分离第一个直系子节点应该足以完成这项工作。由于 Calendar 和 WeekDay 的数据已经存在,我只添加了它们的关系(可用日期和工作日)。在实体框架中完成这项任务非常困难。
解决方法
我无法运行你的代码,因为我没有你的数据库或你的 DbContext 实现,但我会尝试一下。
问题
问题在于这段代码:
data.AvailabilityDates.ToList().ForEach(availability =>
{
Scalable_Web_Data.Models.Calendar from = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(f => f.Id == availability.From.Id).AsNoTracking().FirstOrDefault();
Scalable_Web_Data.Models.Calendar to = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(t => t.Id == availability.To.Id).AsNoTracking().FirstOrDefault();
db.Detatch<Scalable_Web_Data.Models.Calendar>(from);
db.Detatch<WeekDay>(db.WeekDays.Where(w => w.Id == from.DayOfWeek.Id).AsNoTracking().FirstOrDefault());
db.Detatch<Scalable_Web_Data.Models.Calendar>(to);
db.Detatch<WeekDay>(db.WeekDays.Where(w => w.Id == to.DayOfWeek.Id).AsNoTracking().FirstOrDefault());
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,To = to
});
});
您将 Calendar
对象 from
和 to
与 db
分离。在该调用期间,您还特别包括 DayOfWeek
(即 Include(w => w.DayOfWeek)
) - 这将水合 DayOfWeek
对象,您的上下文现在知道该对象(即使您使用 AsNoTracking()
) .看起来您还分离了与 DayOfWeek
和 from
上的子对象匹配的 to
对象。
问题出在这个调用上:
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,To = to
});
您正在创建一个新的 AvailabilityDate
并将分离的 from
和 to
分配给它。您的上下文将这些分离的对象视为分离的,并假定您正在尝试附加它们。在 from
和 to
对象中是您的 DayOfWeek
子对象 - 对于您发布的评论中的 from
和 to
听起来是相同的。然后,您的上下文将尝试附加这两个子 DayOfWeek
对象,但在为 WeekDay
附加 to
对象时将失败,因为 WeekDay
对象带有 相同的 ID 已附加到 from
。上下文不能有两个具有相同 Id 的对象,但它不能识别为同一个对象 - 并且不能将它们识别为同一个对象,因为它们是分离的。
可能的解决方案
为什么要担心分离?实体框架足够聪明,可以知道您正在重用一个对象,而无需重新创建它。只需添加该对象,并允许您的上下文识别它已存在于数据库中,只需将其作为属性添加到新对象中即可。这一切都假设您没有某种唯一索引强制 WeekDay
记录不能链接到多个 Calendar
记录,或者 Calendar
记录不能链接到多个 AvailabilityDate
记录。
data.AvailabilityDates.ToList().ForEach(availability =>
{
Scalable_Web_Data.Models.Calendar from = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(f => f.Id == availability.From.Id).AsNoTracking().FirstOrDefault();
Scalable_Web_Data.Models.Calendar to = db.Calendars.Include(w => w.DayOfWeek).AsNoTracking().Where(t => t.Id == availability.To.Id).AsNoTracking().FirstOrDefault();
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,To = to
});
});
这应该采用现有的 from
和 to
(连同它们已经存在的 DayOfWeek
对象),并使用对现有对象的引用创建一个新的 AvailabilityDate
提供了。
如果这会创建新的 Calendar
或 WeekDay
记录,那么您可能需要重新访问您的 DbContext
实现,特别是对象之间的关系以及如何使用键。我没有看到您在提供的对象上指定任何外键 - 我通常明确定义它们。例如:
public class AvailabilityDate
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int FromId { get; set; }
public int ToId { get; set; }
[ForeignKey(nameof(FromId))]
public virtual Calendar From { get; set; }
[ForeignKey(nameof(ToId))]
public virtual Calendar To { get; set; }
}
这会将 From
和 To
的 Id 作为字段保留在数据库中,然后使用这些外键来组合和填充对象。如果没有指定它们,我不确定 EF 怎么会知道它可以重用对象。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。