C# 使用实体框架和现有子实体在数据库中创建实体时出现问题
我试图使用C#中的实体框架在数据库中插入实体。下面是在数据库中创建场地的函数C# 使用实体框架和现有子实体在数据库中创建实体时出现问题,c#,entity-framework,ef-code-first,inner-classes,C#,Entity Framework,Ef Code First,Inner Classes,我试图使用C#中的实体框架在数据库中插入实体。下面是在数据库中创建场地的函数 public async Task<Guid> CreateVenue(CurrentUser currentUser, CreateVenueDTO data, IAuthenticator authenticator) { using (var db = contextFactory.GetContext()) { var uid = Guid.NewGuid();
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, Venue = venue, 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}相同键值的实例。附着现有实体时,请确保仅附着一个具有给定键值的实体实例。考虑使用“dBraveTopStudioBuffel.EnabelSythViDeAtCug”来查看冲突的键值。
以下是重要的课程
public class Calendar
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DateColumn]
public DateTime Date { get; set; }
public WeekDay DayOfWeek { get; set; }
}
public class WeekDay
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string En { get; set; }
public string No { get; set; }
}
public class AvailabilityDate
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual Calendar From { get; set; }
public virtual Calendar To { get; set; }
}
public class AvailabilityTiming
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid Uid { get; set; }
public WeekDay DayOfWeek { get; set; }
public DayTime From { get; set; }
public DayTime To { get; set; }
public Boolean AvailableAllDay { get; set; }
}
分离第一个直接子对象就足够了。因为日历和工作日的数据已经存在,我只添加它们的关系(可用日期和工作日)。在实体框架中执行此任务非常困难。我无法运行您的代码,因为我没有您的数据库或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
});
});
您正在创建一个新的AvailabilityDate
,并将分离的从和分配给它。您的上下文将这些分离的对象视为已分离,并假定您正在尝试附加它们。在from
和to
对象中,是您的DayOfWeek
子对象-从您发布的评论中,这两个from和to
听起来是相同的。然后,您的上下文将尝试附加这两个子DayOfWeek
对象,但在将的WeekDay
对象附加到时将失败,因为具有相同Id的WeekDay
对象已从附加到。上下文不能有两个ID相同但不能识别为同一对象的对象,也不能识别为同一对象,因为它们是分离的
可能的解决方案
为什么要担心分离呢?实体框架足够聪明,可以知道您正在使用一个对象,而无需重新创建它。只需添加该对象,并允许您的上下文识别该对象已存在于数据库中,只需将其作为属性添加到新对象中即可。这一切都假设您没有某种唯一的索引强制执行工作日
记录不能链接到多个日历
记录,或者日历
记录不能链接到多个可用性日期
记录
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
});
});
这应该将现有的从
和带到
(以及它们已经存在的DayOfWeek
对象),并创建一个新的AvailabilityDate
,其中引用了您提供的现有对象
如果这会创建新的日历
或工作日
记录,则可能需要重新访问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如何知道它可以重用对象。我无法运行您的代码,因为我没有您的数据库或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
});
});
您正在创建一个新的AvailabilityDate
,并将分离的从和分配给它。您的上下文将这些分离的对象视为已分离,并假定您正在尝试附加它们。在from
和to
对象中,是您的DayOfWeek
子对象-从您发布的评论中,这两个from和to
听起来是相同的。然后,您的上下文将尝试附加这两个子DayOfWeek
对象,但在将的WeekDay
对象附加到时将失败,因为具有相同Id的WeekDay
对象已从附加到。上下文不能有两个ID相同但不能识别为同一对象的对象,也不能识别为同一对象,因为它们是分离的
可能的解决方案
为什么要担心分离呢?实体框架足够聪明,可以知道您正在使用一个对象,而无需重新创建它。只需添加该对象,并允许您的上下文识别该对象已存在于数据库中,只需将其作为属性添加到新对象中即可。这一切都假设您没有某种唯一的索引强制执行工作日
记录不能链接到多个日历
记录,或者日历
记录不能链接到多个可用性日期
记录
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
});
});
这应该将现有的从
和带到
(以及它们已经存在的DayOfWeek
对象),并创建一个新的AvailabilityDate
,其中引用了您提供的现有对象
如果这会创建新的日历
或工作日
记录,则可能需要重新访问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; }
}
这会将从
和到
的ID持久化为数据库中的字段,然后使用