В настоящее время у меня есть несколько блоков if-else для обработки различных комбинаций этих флагов, но этот подход не масштабируем и его становится сложно поддерживать, если в будущем будет добавлено больше флагов или условий.
Код: Выделить всё
using Dapper;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace ECommerce.Application.Queries.Handlers;
internal class GetOrdersByFieldsAndFiltersQueryHandler : IRequestHandler
{
private readonly IDbConnection _db;
public GetOrdersByFieldsAndFiltersQueryHandler(IDbConnection dbConnection)
{
_db = dbConnection;
}
public async Task Handle(GetOrdersByFieldsAndFiltersQuery request, CancellationToken cancellationToken)
{
var directFields = request.Fields?.Where(field => !IsNestedField(field)).ToList();
var nestedFields = request.Fields?.Where(field => IsNestedField(field)).ToList();
if (!directFields.Contains("order_number", StringComparer.OrdinalIgnoreCase))
{
directFields.Add("order_number");
}
// Convert direct field names to snake_case
var fieldSelection = directFields == null || directFields.Count == 0
? "o.*"
: string.Join(", ", directFields.Select(field => $"o.{ToSnakeCase(field.Trim())}"));
string filterCondition = "";
DynamicParameters parameters = new DynamicParameters();
if (request.Filters != null && request.Filters.Any() == true)
{
var filterResult = CreateFilterCondition(request.Filters);
filterCondition = filterResult.SqlWhereClause;
parameters = filterResult.Parameters;
}
var query = new StringBuilder();
query.AppendLine($"SELECT {fieldSelection} ");
if (request.IncludeOrderDetails)
{
query.AppendLine(", od.order_number AS OrderNumber, od.*");
}
if (request.IncludeShippingInfo)
{
query.AppendLine(", sh.order_number AS ShippingOrderNumber, sh.*");
}
query.AppendLine("FROM orders AS o");
if (request.IncludeOrderDetails)
{
query.AppendLine("LEFT JOIN order_details od ON od.order_number = o.order_number");
}
if (request.IncludeShippingInfo)
{
query.AppendLine("LEFT JOIN shipping_info sh ON sh.order_number = o.order_number");
}
if (!string.IsNullOrEmpty(filterCondition))
{
query.AppendLine(filterCondition);
}
string splitOn = "OrderNumber";
if (request.IncludeOrderDetails && request.IncludeShippingInfo)
{
splitOn += ",ShippingOrderNumber";
}
else if (!request.IncludeOrderDetails && request.IncludeShippingInfo)
{
splitOn = "ShippingOrderNumber";
}
var orderDictionary = new Dictionary();
if (request.IncludeOrderDetails && request.IncludeShippingInfo)
{
var queryResult = await _db.QueryAsync(
query.ToString(),
(order, detail, shipping) =>
{
Order currentOrder = order;
if (!orderDictionary.TryGetValue(order.OrderNumber, out currentOrder))
{
currentOrder = order;
orderDictionary.Add(currentOrder.OrderNumber!, currentOrder);
}
if (detail != null)
{
currentOrder.OrderDetails = currentOrder.OrderDetails ?? new List();
currentOrder.OrderDetails.Add(detail);
}
if (shipping != null)
{
currentOrder.ShippingInfo = currentOrder.ShippingInfo ?? new ShippingInfo();
currentOrder.ShippingInfo = shipping;
}
return currentOrder;
},
parameters,
splitOn: splitOn
);
}
else if (!request.IncludeOrderDetails && request.IncludeShippingInfo)
{
var queryResult = await _db.QueryAsync(
query.ToString(),
(order, shipping) =>
{
Order currentOrder = order;
if (!orderDictionary.TryGetValue(order.OrderNumber, out currentOrder))
{
currentOrder = order;
orderDictionary.Add(currentOrder.OrderNumber!, currentOrder);
}
if (shipping != null)
{
currentOrder.ShippingInfo = currentOrder.ShippingInfo ?? new ShippingInfo();
currentOrder.ShippingInfo = shipping;
}
return currentOrder;
},
parameters,
splitOn: splitOn
);
}
else if (request.IncludeOrderDetails && !request.IncludeShippingInfo)
{
var queryResult = await _db.QueryAsync(
query.ToString(),
(order, detail) =>
{
Order currentOrder = order;
if (!orderDictionary.TryGetValue(order.OrderNumber, out currentOrder))
{
currentOrder = order;
orderDictionary.Add(currentOrder.OrderNumber!, currentOrder);
}
if (detail != null)
{
currentOrder.OrderDetails = currentOrder.OrderDetails ?? new List();
currentOrder.OrderDetails.Add(detail);
}
return currentOrder;
},
parameters,
splitOn: splitOn
);
}
else
{
// Query for orders without order details or shipping info
var queryResult = await _db.QueryAsync(
query.ToString(),
parameters
);
foreach (var order in queryResult)
{
if (!orderDictionary.ContainsKey(order.OrderNumber))
{
orderDictionary.Add(order.OrderNumber, order);
}
}
}
return orderDictionary.Values.ToList();
}
private bool IsNestedField(string field)
{
return field.Equals("order_details", StringComparison.OrdinalIgnoreCase) ||
field.Equals("shipping_info", StringComparison.OrdinalIgnoreCase);
}
public static (string SqlWhereClause, DynamicParameters Parameters) CreateFilterCondition(IEnumerable filters)
{
var parameters = new DynamicParameters();
var filterIndex = 0;
var joinedFilters = string.Join(" AND ", filters.Select(filter =>
{
var parts = filter.Split(new[] { '=' }, 2);
if (parts.Length != 2) return filter; // In case of a malformed filter
var column = Regex.Replace(parts[0].Trim(), @"\b[a-z][a-zA-Z]*\b", match =>
{
return ToSnakeCase($"o.{match.Value}");
});
var parameterName = $"@param{filterIndex}";
parameters.Add(parameterName, parts[1].Trim().Trim('\'')); // Add to parameters
filterIndex++;
return $"{column} = {parameterName}";
}));
return ($" WHERE {joinedFilters}", parameters);
}
private static string ToSnakeCase(string input)
{
return Regex.Replace(input, "([a-z])([A-Z])", "$1_$2").ToLower();
}
}
Можно ли реализовать этот сценарий в Dapper?
Подробнее здесь: https://stackoverflow.com/questions/789 ... iple-flags