33using System . Linq ;
44using System . Threading . Tasks ;
55using Microsoft . EntityFrameworkCore ;
6+ using Microsoft . EntityFrameworkCore . ChangeTracking ;
67using VirtoCommerce . OrdersModule . Core . Model ;
78using VirtoCommerce . OrdersModule . Data . Model ;
89using VirtoCommerce . Platform . Core . Common ;
@@ -17,6 +18,9 @@ public class OrderRepository : DbContextRepositoryBase<OrderDbContext>, IOrderRe
1718 public OrderRepository ( OrderDbContext dbContext , IUnitOfWork unitOfWork = null )
1819 : base ( dbContext , unitOfWork )
1920 {
21+ // Resolves Breaking changes in EF Core 7.0 (EF7) when EF Core will not automatically delete orphans because all FKs are nullable.
22+ // https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes?tabs=v7#orphaned-dependents-of-optional-relationships-are-not-automatically-deleted
23+ dbContext . SavingChanges += OnSavingChanges ;
2024 }
2125
2226 public IQueryable < CustomerOrderEntity > CustomerOrders => DbContext . Set < CustomerOrderEntity > ( ) ;
@@ -261,5 +265,72 @@ public virtual async Task RemoveOrdersByIdsAsync(IList<string> ids)
261265 }
262266 }
263267#pragma warning restore S109
268+
269+ protected override void Dispose ( bool disposing )
270+ {
271+ if ( disposing && DbContext != null )
272+ {
273+ DbContext . SavingChanges -= OnSavingChanges ;
274+ }
275+ base . Dispose ( disposing ) ;
276+ }
277+
278+ protected virtual bool IsOrphanedEntity ( EntityEntry entry )
279+ {
280+ switch ( entry . Entity )
281+ {
282+ case CaptureEntity capture
283+ when capture . PaymentId == null :
284+ case RefundEntity refund
285+ when refund . PaymentId == null :
286+ case PaymentInEntity payment
287+ when payment . CustomerOrderId == null
288+ && payment . ShipmentId == null :
289+ case AddressEntity a
290+ when a . CustomerOrderId == null
291+ && a . ShipmentId == null
292+ && a . PaymentInId == null :
293+ case DiscountEntity d
294+ when d . CustomerOrderId == null
295+ && d . ShipmentId == null
296+ && d . LineItemId == null
297+ && d . PaymentInId == null :
298+ case TaxDetailEntity t
299+ when t . CustomerOrderId == null
300+ && t . ShipmentId == null
301+ && t . LineItemId == null
302+ && t . PaymentInId == null :
303+ case FeeDetailEntity f
304+ when f . CustomerOrderId == null
305+ && f . ShipmentId == null
306+ && f . LineItemId == null
307+ && f . PaymentInId == null :
308+ case OrderDynamicPropertyObjectValueEntity v
309+ when v . CustomerOrderId == null
310+ && v . PaymentInId == null
311+ && v . ShipmentId == null
312+ && v . RefundId == null
313+ && v . LineItemId == null
314+ && v . CaptureId == null :
315+ return true ;
316+ }
317+
318+ return false ;
319+ }
320+
321+ private void OnSavingChanges ( object sender , SavingChangesEventArgs args )
322+ {
323+ var ctx = ( DbContext ) sender ;
324+ var entries = ctx . ChangeTracker . Entries ( ) ;
325+
326+ foreach ( var entry in entries )
327+ {
328+ if ( entry . State == EntityState . Modified &&
329+ IsOrphanedEntity ( entry ) )
330+ {
331+ entry . State = EntityState . Deleted ;
332+ }
333+ }
334+ }
264335 }
265336}
0 commit comments