Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 167 additions & 140 deletions src/VirtoCommerce.OrdersModule.Data/Repositories/OrderRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace VirtoCommerce.OrdersModule.Data.Repositories
{
public class OrderRepository : DbContextRepositoryBase<OrderDbContext>, IOrderRepository
{
#pragma warning disable S109
public OrderRepository(OrderDbContext dbContext, IUnitOfWork unitOfWork = null)
: base(dbContext, unitOfWork)
{
Expand Down Expand Up @@ -50,202 +49,231 @@ public virtual async Task<IList<CustomerOrderEntity>> GetCustomerOrdersByIdsAsyn
return Array.Empty<CustomerOrderEntity>();
}

var result = await CustomerOrders.Where(x => ids.Contains(x.Id)).ToArrayAsync();
var customerOrderResponseGroup = EnumUtility.SafeParseFlags(responseGroup, CustomerOrderResponseGroup.Full);

// Load CustomerOrder + the child collections that hang directly off it in a single
// round-trip. EF Core wires up navigations on InPayments/Items/Shipments that are
// loaded below by foreign-key fix-up.
var rootQuery = CustomerOrders
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.AsQueryable();

if (!result.Any())
if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithAddresses))
{
return Array.Empty<CustomerOrderEntity>();
rootQuery = rootQuery.Include(x => x.Addresses);
}

ids = result.Select(x => x.Id).ToArray();

await Discounts.Where(x => ids.Contains(x.CustomerOrderId)).LoadAsync();
await TaxDetails.Where(x => ids.Contains(x.CustomerOrderId)).LoadAsync();
await FeeDetails.Where(x => ids.Contains(x.CustomerOrderId)).LoadAsync();

var customerOrderResponseGroup = EnumUtility.SafeParseFlags(responseGroup, CustomerOrderResponseGroup.Full);

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => ids.Contains(x.CustomerOrderId)).LoadAsync();
rootQuery = rootQuery.Include(x => x.DynamicPropertyObjectValues);
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithAddresses))
var result = await rootQuery
.Where(x => ids.Contains(x.Id))
.AsSplitQuery()
.ToArrayAsync();

if (result.Length == 0)
{
await Addresses.Where(x => ids.Contains(x.CustomerOrderId)).LoadAsync();
return Array.Empty<CustomerOrderEntity>();
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithInPayments))
{
var payments = await InPayments.Where(x => ids.Contains(x.CustomerOrderId)).ToArrayAsync();
ids = result.Select(x => x.Id).ToArray();

if (payments.Any())
{
var paymentIds = payments.Select(x => x.Id).ToArray();

await Discounts.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();
await TaxDetails.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();
await FeeDetails.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();
await Addresses.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();
await Transactions.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => paymentIds.Contains(x.PaymentInId)).LoadAsync();
}
}
}
await LoadInPayments(ids, customerOrderResponseGroup);
await LoadLineItems(ids, customerOrderResponseGroup);
await LoadShipments(ids, customerOrderResponseGroup);
await LoadRefunds(ids, customerOrderResponseGroup);
await LoadCaptures(ids, customerOrderResponseGroup);

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithItems))
if (!customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithPrices))
{
var lineItems = await LineItems
.Where(x => ids.Contains(x.CustomerOrderId))
.ToArrayAsync();

if (lineItems.Length > 0)
foreach (var customerOrder in result)
{
var lineItemIds = lineItems.Select(x => x.Id).ToArray();

await Discounts.Where(x => lineItemIds.Contains(x.LineItemId)).LoadAsync();
await TaxDetails.Where(x => lineItemIds.Contains(x.LineItemId)).LoadAsync();
await FeeDetails.Where(x => lineItemIds.Contains(x.LineItemId)).LoadAsync();

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => lineItemIds.Contains(x.LineItemId)).LoadAsync();
}

var configurationItemIds = lineItems.Where(x => x.IsConfigured).Select(x => x.Id).ToArray();
if (configurationItemIds.Length > 0)
{
await ConfigurationItems
.Where(x => configurationItemIds.Contains(x.LineItemId))
.Include(x => x.Files)
.AsSplitQuery()
.LoadAsync();
}
customerOrder.ResetPrices();
}
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithShipments))
return result;
}

public virtual async Task<IList<PaymentInEntity>> GetPaymentsByIdsAsync(IList<string> ids)
{
if (ids.IsNullOrEmpty())
{
var shipments = await Shipments.Where(x => ids.Contains(x.CustomerOrderId)).ToArrayAsync();
return Array.Empty<PaymentInEntity>();
}

if (shipments.Any())
{
var shipmentIds = shipments.Select(x => x.Id).ToArray();

await Discounts.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
await TaxDetails.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
await FeeDetails.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
await Addresses.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
await ShipmentItems.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
await ShipmentPackagesPackages.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => shipmentIds.Contains(x.ShipmentId)).LoadAsync();
}
}
var result = await InPayments
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.Include(x => x.Addresses)
.Include(x => x.Transactions)
.Include(x => x.DynamicPropertyObjectValues)
.Where(x => ids.Contains(x.Id))
.AsSplitQuery()
.ToArrayAsync();

return result.Length != 0 ? result : Array.Empty<PaymentInEntity>();
}

public virtual async Task<IList<ShipmentEntity>> GetShipmentsByIdsAsync(IList<string> ids)
{
if (ids.IsNullOrEmpty())
{
return Array.Empty<ShipmentEntity>();
}

var result = await Shipments
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.Include(x => x.Addresses)
.Include(x => x.Items)
.Include(x => x.Packages)
.Include(x => x.DynamicPropertyObjectValues)
.Where(x => ids.Contains(x.Id))
.AsSplitQuery()
.ToArrayAsync();

return result.Length != 0 ? result : Array.Empty<ShipmentEntity>();
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithRefunds))
protected virtual async Task LoadInPayments(IList<string> orderIds, CustomerOrderResponseGroup responseGroup)
{
if (!responseGroup.HasFlag(CustomerOrderResponseGroup.WithInPayments))
{
var refunds = await Refunds.Where(x => ids.Contains(x.CustomerOrderId)).ToArrayAsync();
return;
}

if (refunds.Any())
{
var refundIds = refunds.Select(x => x.Id).ToArray();
var query = InPayments
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.Include(x => x.Addresses)
.Include(x => x.Transactions)
.AsQueryable();

await RefundItems.Where(x => refundIds.Contains(x.RefundId)).LoadAsync();
if (responseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
query = query.Include(x => x.DynamicPropertyObjectValues);
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => refundIds.Contains(x.RefundId)).LoadAsync();
}
}
await query
.Where(x => orderIds.Contains(x.CustomerOrderId))
.AsSplitQuery()
.LoadAsync();
}

protected virtual async Task LoadLineItems(IList<string> orderIds, CustomerOrderResponseGroup responseGroup)
{
if (!responseGroup.HasFlag(CustomerOrderResponseGroup.WithItems))
{
return;
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithCaptures))
var query = LineItems
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.AsQueryable();

if (responseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
var captures = await Captures.Where(x => ids.Contains(x.CustomerOrderId)).ToArrayAsync();
query = query.Include(x => x.DynamicPropertyObjectValues);
}

if (captures.Any())
{
var captureIds = captures.Select(x => x.Id).ToArray();
var lineItems = await query
.Where(x => orderIds.Contains(x.CustomerOrderId))
.AsSplitQuery()
.ToArrayAsync();

await CaptureItems.Where(x => captureIds.Contains(x.CaptureId)).LoadAsync();
// ConfigurationItem.Files is two levels deeper than LineItem; keeping it as a
// dedicated query avoids growing the LineItem join tree (and the cartesian
// explosion under AsSingleQuery semantics).
var configurationItemIds = lineItems.Where(x => x.IsConfigured).Select(x => x.Id).ToArray();
if (configurationItemIds.Length > 0)
{
await ConfigurationItems
.Include(x => x.Files)
.Where(x => configurationItemIds.Contains(x.LineItemId))
.AsSplitQuery()
.LoadAsync();
}
}

if (customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
await OrderDynamicPropertyObjectValues.Where(x => captureIds.Contains(x.CaptureId)).LoadAsync();
}
}
protected virtual async Task LoadShipments(IList<string> orderIds, CustomerOrderResponseGroup responseGroup)
{
if (!responseGroup.HasFlag(CustomerOrderResponseGroup.WithShipments))
{
return;
}

if (!customerOrderResponseGroup.HasFlag(CustomerOrderResponseGroup.WithPrices))
var query = Shipments
.Include(x => x.Discounts)
.Include(x => x.TaxDetails)
.Include(x => x.FeeDetails)
.Include(x => x.Addresses)
.Include(x => x.Items)
.Include(x => x.Packages)
.AsQueryable();

if (responseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
foreach (var customerOrder in result)
{
customerOrder.ResetPrices();
}
query = query.Include(x => x.DynamicPropertyObjectValues);
}

return result;
await query
.Where(x => orderIds.Contains(x.CustomerOrderId))
.AsSplitQuery()
.LoadAsync();
}

public virtual async Task<IList<PaymentInEntity>> GetPaymentsByIdsAsync(IList<string> ids)
protected virtual async Task LoadRefunds(IList<string> orderIds, CustomerOrderResponseGroup responseGroup)
{
if (ids.IsNullOrEmpty())
if (!responseGroup.HasFlag(CustomerOrderResponseGroup.WithRefunds))
{
return Array.Empty<PaymentInEntity>();
return;
}

var result = await InPayments.Where(x => ids.Contains(x.Id)).ToArrayAsync();
var query = Refunds
.Include(x => x.Items)
.AsQueryable();

if (!result.Any())
if (responseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
return Array.Empty<PaymentInEntity>();
query = query.Include(x => x.DynamicPropertyObjectValues);
}

ids = result.Select(x => x.Id).ToArray();

await Discounts.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();
await TaxDetails.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();
await FeeDetails.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();
await Addresses.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();
await Transactions.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();
await OrderDynamicPropertyObjectValues.Where(x => ids.Contains(x.PaymentInId)).LoadAsync();

return result;
await query
.Where(x => orderIds.Contains(x.CustomerOrderId))
.AsSplitQuery()
.LoadAsync();
}

public virtual async Task<IList<ShipmentEntity>> GetShipmentsByIdsAsync(IList<string> ids)
protected virtual async Task LoadCaptures(IList<string> orderIds, CustomerOrderResponseGroup responseGroup)
{
if (ids.IsNullOrEmpty())
if (!responseGroup.HasFlag(CustomerOrderResponseGroup.WithCaptures))
{
return Array.Empty<ShipmentEntity>();
return;
}

var result = await Shipments.Where(x => ids.Contains(x.Id)).ToArrayAsync();
var query = Captures
.Include(x => x.Items)
.AsQueryable();

if (!result.Any())
if (responseGroup.HasFlag(CustomerOrderResponseGroup.WithDynamicProperties))
{
return Array.Empty<ShipmentEntity>();
query = query.Include(x => x.DynamicPropertyObjectValues);
}

ids = result.Select(x => x.Id).ToArray();

await Discounts.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await TaxDetails.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await FeeDetails.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await Addresses.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await ShipmentItems.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await ShipmentPackagesPackages.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();
await OrderDynamicPropertyObjectValues.Where(x => ids.Contains(x.ShipmentId)).LoadAsync();

return result;
await query
.Where(x => orderIds.Contains(x.CustomerOrderId))
.AsSplitQuery()
.LoadAsync();
}

public void PatchRowVersion(CustomerOrderEntity entity, byte[] rowVersion)
Expand All @@ -264,7 +292,6 @@ public virtual async Task RemoveOrdersByIdsAsync(IList<string> ids)
Remove(order);
}
}
#pragma warning restore S109

protected override void Dispose(bool disposing)
{
Expand Down
Loading