我在使用 Entity Framework Core 的 ASP.NET Core 应用程序中遇到问题,反复收到以下响应:
{
"statusCode": 500,
"message": "The instance of entity type 'Airline' cannot be tracked because another instance with the key value '{CarrierCode: DY}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
}
这发生在我的 PassengerController 中的 AddBaggage() 方法的 foreach 循环的第二次迭代期间,如下行:
await _baggageRepository.AddAsync(newBaggage);
。方法如下:
[HttpPost("{id}/flight/{flightId}/add-baggage")]
public async Task<ActionResult<Baggage>> AddBaggage(Guid id, int flightId,
[FromBody] List<AddBaggageModel> addBaggageModels)
{
var passenger = await _passengerRepository.GetPassengerByIdAsync(id);
var selectedFlight = await _flightRepository.GetFlightByIdAsync(flightId);
var destination = await _destinationRepository.GetDestinationByCriteriaAsync(f =>
f.IATAAirportCode == addBaggageModels.FirstOrDefault().FinalDestination);
var baggageList = new List<Baggage>();
foreach (var baggageModel in addBaggageModels)
{
var newBaggage = new Baggage
{
PassengerId = passenger.Id,
Weight = baggageModel.Weight,
DestinationId = destination.IATAAirportCode
};
//...some code
else if (baggageModel.TagType == TagTypeEnum.System)
{
var number = _baggageRepository.GetNextSequenceValue("BaggageTagsSequence");
var baggageTag = new BaggageTag(selectedFlight.ScheduledFlight.Airline, number);
newBaggage.BaggageTag = baggageTag;
}
await _baggageRepository.AddAsync(newBaggage); // An exception is thrown here, on the second iteration, when we attempt to assign a reference to the same object/instance of Airline as in the first iteration in the constructor parameter.
baggageList.Add(newBaggage);
var orderedFlights = passenger.Flights
.Where(f => f.Flight.DepartureDateTime >= selectedFlight.DepartureDateTime)
.OrderBy(f => f.Flight.DepartureDateTime)
.ToList();
foreach (var connectingFlight in orderedFlights)
{
//...some code
FlightBaggage flightBaggage = new FlightBaggage
{
Flight = connectingFlight.Flight,
Baggage = newBaggage,
BaggageType = baggageType
};
newBaggage.Flights.Add(flightBaggage);
//...some code
}
}
await _baggageRepository.UpdateAsync(baggageList.ToArray());
return Ok();
}
这也是 BaggageTag 构造函数之一:
public BaggageTag(Airline? airline, int number)
{
Airline = airline;
LeadingDigit = 0;
Number = number;
TagNumber = _GetBaggageTagNumber();
TagType = TagTypeEnum.System;
}
我的 PassengerRepository 和 FlightRepository 中也有以下代码:
public async Task<Flight> GetFlightByIdAsync(int id)
{
var CACHE_KEY = $"Flight_{id}";
if (!_cache.TryGetValue(CACHE_KEY, out Flight flight))
{
flight = await _context.Flights.AsNoTracking()
.Include(_ => _.ScheduledFlight)
.ThenInclude(_ => _.DestinationFrom)
.Include(_ => _.ScheduledFlight)
.ThenInclude(_ => _.DestinationTo)
.Include(_ => _.ScheduledFlight)
.ThenInclude(_ => _.Airline)
.Include(_ => _.Aircraft)
.ThenInclude(_ => _.AircraftType)
.Include(_ => _.Boarding)
.Include(_ => _.ListOfBookedPassengers)
.ThenInclude(_ => _.Passenger)
.Include(_ => _.ListOfCheckedBaggage)
.ThenInclude(_ => _.Baggage)
.Include(_ => _.Seats)
.FirstOrDefaultAsync(_ => _.Id == id);
if (flight != null)
{
_cache.Set(CACHE_KEY, flight, new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(10)
});
}
}
return flight;
}
public async Task<Passenger> GetPassengerByIdAsync(Guid id)
{
return await _context.Passengers.AsNoTracking()
.Include(_ => _.PNR)
.Include(_ => _.FrequentFlyer)
.Include(_ => _.TravelDocuments)
.Include(_ => _.PassengerCheckedBags)
.ThenInclude(_ => _.BaggageTag)
.Include(_ => _.PassengerCheckedBags)
.ThenInclude(_ => _.SpecialBag)
.Include(_ => _.Flights)
.ThenInclude(_ => _.Flight)
.ThenInclude(_ => _.ScheduledFlight)
.ThenInclude(_ => _.Airline)
.Include(_ => _.Comments)
.ThenInclude(_ => _.PredefinedComment)
.Include(_ => _.AssignedSeats)
.Include(_ => _.SpecialServiceRequests)
.ThenInclude(_ => _.SSRCode)
.FirstOrDefaultAsync(_ => _.Id == id);
}
我了解该问题源于 ChangeTracker 尝试附加已在上下文中跟踪的实体。但是,我不确定如何有效地避免这种情况。我读过有关使用
dbContext.Entry(entity).State = EntityState.Detached
分离实体的内容,但我不确定在哪里放置这行代码。
我应该将其添加到 PassengerRepository、FlightRepository 或 GenericRepository 中的 AddAsync()、UpdateAsync() 和 DeleteAsync() 等方法中吗?或者,我应该重写 AppDbContext 中的 SaveChangesAsync() 方法并将其包含在那里吗?
我非常感谢任何能够有效解决此问题的指导或替代解决方案。谢谢您的帮助!
我尝试从我的存储库中删除所有 AsNoTracking() 和 AsQueryable() 方法,并且一切正常。然而,我最初添加这些方法是为了减少内存开销,从那时起,我就一直遇到这个问题。
我想到了一些问题,但主要的怀疑是您的存储库方法正在使用
AsNoTracking()
(即 selectedFlight.ScheduledFlight.Airline)获取数据,但您试图将这些数据与此预订和关联属性的新实例相关联。当加载要关联的实体时,您不想使用 AsNoTracking()
,因为当 DbContext
可能已经在跟踪其他实例时,这将返回一个新的、分离的实体实例。这可能会导致您所看到的错误,或者由于索引限制而出现异常的情况,或者插入具有新 ID 的重复记录。 (它尝试插入关联的未跟踪实例)删除获取查询中的 AsNoTracking()
可能会解决该问题。
这是我不建议使用这样的存储库模式的原因之一,因为通过存储库的每个调用都是该存储库方法的单独关注点。有些调用可能需要相关数据,有些可能不需要。加载所有内容可能会很昂贵,因此“快速修复”可能是添加一个
AsNoTracking()
来提高性能,但这会导致意外的问题,其他代码(例如像这样的更新)将期望处理跟踪的引用。如果代码使用内置存储库模式 (DbSet),那么消费者可以更好地管理要包含的相关数据并优化 AsNoTracking()
以实现只读操作。