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 > ( ) ;
@@ -247,5 +251,72 @@ public virtual async Task RemoveOrdersByIdsAsync(IList<string> ids)
247251 }
248252 }
249253#pragma warning restore S109
254+
255+ protected override void Dispose ( bool disposing )
256+ {
257+ if ( disposing && DbContext != null )
258+ {
259+ DbContext . SavingChanges -= OnSavingChanges ;
260+ }
261+ base . Dispose ( disposing ) ;
262+ }
263+
264+ protected virtual bool IsOrphanedEntity ( EntityEntry entry )
265+ {
266+ switch ( entry . Entity )
267+ {
268+ case CaptureEntity capture
269+ when capture . PaymentId == null :
270+ case RefundEntity refund
271+ when refund . PaymentId == null :
272+ case PaymentInEntity payment
273+ when payment . CustomerOrderId == null
274+ && payment . ShipmentId == null :
275+ case AddressEntity a
276+ when a . CustomerOrderId == null
277+ && a . ShipmentId == null
278+ && a . PaymentInId == null :
279+ case DiscountEntity d
280+ when d . CustomerOrderId == null
281+ && d . ShipmentId == null
282+ && d . LineItemId == null
283+ && d . PaymentInId == null :
284+ case TaxDetailEntity t
285+ when t . CustomerOrderId == null
286+ && t . ShipmentId == null
287+ && t . LineItemId == null
288+ && t . PaymentInId == null :
289+ case FeeDetailEntity f
290+ when f . CustomerOrderId == null
291+ && f . ShipmentId == null
292+ && f . LineItemId == null
293+ && f . PaymentInId == null :
294+ case OrderDynamicPropertyObjectValueEntity v
295+ when v . CustomerOrderId == null
296+ && v . PaymentInId == null
297+ && v . ShipmentId == null
298+ && v . RefundId == null
299+ && v . LineItemId == null
300+ && v . CaptureId == null :
301+ return true ;
302+ }
303+
304+ return false ;
305+ }
306+
307+ private void OnSavingChanges ( object sender , SavingChangesEventArgs args )
308+ {
309+ var ctx = ( DbContext ) sender ;
310+ var entries = ctx . ChangeTracker . Entries ( ) ;
311+
312+ foreach ( var entry in entries )
313+ {
314+ if ( entry . State == EntityState . Modified &&
315+ IsOrphanedEntity ( entry ) )
316+ {
317+ entry . State = EntityState . Deleted ;
318+ }
319+ }
320+ }
250321 }
251322}
0 commit comments