![](/img/trans.png)
[英]Connect parent entity with existing child entity in database using Entity Framework 6
[英]Problem creating an entity in the database using Entity Framework with existing child entities
我正在嘗試使用 C# 中的實體框架在數據庫中插入一個實體。 這是在數據庫中創建場地的 function。
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;
}
}
在插入可用日期時
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,
To = to
});
我收到此錯誤(即使我可能已經分離了所有子實體)
無法跟蹤實體類型“WeekDay”的實例,因為已經在跟蹤具有相同鍵值 {'Id'} 的另一個實例。 附加現有實體時,請確保僅附加一個具有給定鍵值的實體實例。 考慮使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”來查看沖突的鍵值。
這是重要的課程
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; }
}
分離第一個直系孩子以完成這項工作就足夠了。 由於 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
});
});
您to
Calendar
對象from
db
分離。 在該通話期間,您還特別包括DayOfWeek
(即Include(w => w.DayOfWeek)
) - 這將為您的上下文現在知道的DayOfWeek
object 提供水分(即使您使用AsNoTracking()
)。 看起來您還分離了與from
和to
上的子對象匹配的DayOfWeek
對象。
問題在於這個電話:
venue.VenueAvailabilityDates.Add(new AvailabilityDate
{
From = from,
To = to
});
您正在創建一個新的AvailabilityDate
並分配 detached from
和to
它。 您的上下文將這些分離的對象視為分離的,並假定您正在嘗試附加它們。 在from
和to
對象中是您的DayOfWeek
子 object - 這聽起來與您發布的評論的from
和to
相同。 然后,您的上下文將嘗試附加這兩個DayOfWeek
子對象,但在附加WeekDay
object for to
時將失敗,因為具有相同 Id的WeekDay
object 已經附加到from
。 上下文不能有兩個具有相同 ID 的對象,它不能識別為相同的 object - 並且它不能將它們識別為相同的 object,因為它們是分離的。
一個可能的解決方案
為什么要擔心分離呢? Entity Framework 足夠聰明,可以知道您正在重用 object 而無需重新創建它。 只需添加 object,並允許您的上下文識別它已經存在於數據庫中,只需將其作為屬性添加到您的新 object。 這一切都假定您沒有某種唯一索引來強制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 怎么會知道它可以重用對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.