I just made a working Get method for Orders combining two methods:
This method gets all orders and data associated with it:
private async Task<(List<Order>, int)> GetOrdersWithDataAsync(NpgsqlConnection connection, NpgsqlTransaction transaction, OrderFilter filtering, Sorting sorting) { List<Order> orders = new List<Order>(); int totalCount = 0; StringBuilder cmdBuilder = new StringBuilder("select o.\"Id\", o.\"ShippingAddressId\", o.\"BillingAddressId\", o.\"PersonId\", o.\"TotalPrice\", " + " pe.\"Id\" as \"PersonId\", pe.\"FirstName\", pe.\"LastName\", pe.\"Email\", sa.\"Id\" as \"ShippingAddressId\" sa.\"StreetName\" as \"ShippingStreetName\", " + "sa.\"StreetNumber\" as \"ShippingStreetNumber\",sa.\"City\" as \"ShippingCity\", sa.\"Zipcode\" as \"ShippingZipcode\", " + "ba.\"Id\" as \"BillingAddressId\", ba.\"StreetName\" as \"BillingStreetName\", ba.\"StreetNumber\" as \"BillingStreetNumber\", ba.\"City\" as \"BillingCity\", " + "ba.\"Zipcode\" as \"BillingZipcode\", Count(*) Over() as \"TotalCount\" from \"Order\" o " + "inner join \"Person\" pe on o.\"PersonId\" = pe.\"Id\" inner join \"Address\" sa on sa.\"Id\" = o.\"ShippingAddressId\" inner join \"Address\" ba on ba.\"Id\" = " + "o.\"BillingAddressId\""); NpgsqlCommand cmd = new NpgsqlCommand("" , connection); cmdBuilder.Append(" WHERE 1 = 1"); if(!string.IsNullOrEmpty(filtering.SearchQuery)) { cmdBuilder.Append(" AND pe.\"FirstName\" LIKE \'%@SearchQuery%\' OR pe.\"LastName\" LIKE \'%@SearchQuery%\'"); cmd.Parameters.AddWithValue("@SearchQuery", filtering.SearchQuery); } if(filtering.MinPrice.HasValue && filtering.MinPrice.Value != 0) { cmdBuilder.Append(" AND o.\"TotalPrice\" > @MinPrice"); cmd.Parameters.AddWithValue("@MinPrice", filtering.MinPrice); } if (filtering.MaxPrice.HasValue && filtering.MaxPrice.Value != 0) { cmdBuilder.Append(" AND o.\"TotalPrice\" < @MaxPrice"); cmd.Parameters.AddWithValue("@MaxPrice", filtering.MaxPrice); } cmd.CommandText = cmdBuilder.ToString(); cmd.Transaction = transaction; cmd.Connection = connection; NpgsqlDataReader reader = await cmd.ExecuteReaderAsync(); try { using (reader) { if (reader.HasRows) { while (reader.Read()) { Guid orderId = (Guid)reader["Id"]; Order order = new Order(); order.ShippingAddress = new Address(); order.BillingAddress = new Address(); order.Products = new List<Product>(); order.Id = (Guid)reader["Id"]; order.TotalPrice = (decimal)reader["TotalPrice"]; order.Person = new Person(); order.Person.Id = (Guid)reader["PersonId"]; order.Person.FirstName = (string)reader["FirstName"]; order.Person.LastName = (string)reader["LastName"]; order.Person.Email = (string)reader["Email"]; order.PersonId = (Guid)reader["PersonId"]; order.ShippingAddress.Id = (Guid)reader["ShippingAddressId"]; order.ShippingAddress.StreetName = (string)reader["ShippingStreetName"]; order.ShippingAddress.StreetNumber = (string)reader["ShippingStreetNumber"]; order.ShippingAddress.City = (string)reader["ShippingCity"]; order.ShippingAddress.ZipCode = (int)reader["ShippingZipcode"]; order.BillingAddress.Id = (Guid)reader["BillingAddressId"]; order.BillingAddress.StreetName = (string)reader["BillingStreetName"]; order.BillingAddress.StreetNumber = (string)reader["BillingStreetNumber"]; order.BillingAddress.City = (string)reader["BillingCity"]; order.BillingAddress.ZipCode = (int)reader["BillingZipcode"]; orders.Add(order); } } return (orders, totalCount); } } catch(Exception ex) { throw ex; } }
A method that gets products for each order from the product repository which I injected into OrderRepository:
public async Task<List<Order>> GetProductsByOrderIdAsync(List<Order> orders, NpgsqlConnection connection, NpgsqlTransaction transaction) { foreach (var order in orders) { List<Product> products = new List<Product>(); try { NpgsqlCommand cmd = new NpgsqlCommand("SELECT p.\"Id\", p.\"Name\", p.\"Price\", po.\"ProductQty\" as \"Quantity\" FROM \"Product\" as p INNER JOIN \"ProductOrder\" as po ON p.\"Id\" = po.\"ProductId\" WHERE po.\"OrderId\" = @id", connection); cmd.Parameters.AddWithValue("id", order.Id); cmd.Transaction = transaction; cmd.Connection = connection; NpgsqlDataReader reader = await cmd.ExecuteReaderAsync(); if (reader.HasRows) { while (reader.Read()) { Product product = new Product(); product.Id = (Guid)reader["Id"]; product.Name = (string)reader["Name"]; product.Price = (decimal)reader["Price"]; product.Quantity = (int)reader["Quantity"]; products.Add(product); } } reader.Close(); } catch (Exception ex) { throw ex; } order.Products = products; } return orders; }
Both of these are done through this one:
public async Task<PagedList<Order>> GetOrdersAsync(Paginate paginate, OrderFilter filtering, Sorting sorting)
{
NpgsqlConnection connection = new NpgsqlConnection(_connectionProvider.GetConnectionString());
await connection.OpenAsync();
try
{
using (connection)
{
NpgsqlTransaction transaction = connection.BeginTransaction();
try
{
(List<Order> orders, int totalCount) = await GetOrdersWithDataAsync(connection, transaction, filtering, sorting);
List<Order> ordersWithProducts = await _productRepository.GetProductsByOrderIdAsync(orders, connection, transaction);
PagedList<Order> pagedOrders = new PagedList<Order>()
{
Results = ordersWithProducts,
CurrentPage = paginate.PageNumber,
PageSize = paginate.PageSize,
TotalCount = orders.Count
};
if (pagedOrders != null && pagedOrders.TotalCount > 0)
{
transaction.Commit();
return pagedOrders;
}
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
}
}
catch (Exception ex)
{
throw ex;
}
return null;
}
The thing that worries me is the select statements for each order in the GetProductsByOrderId method, is there a better way to do this? I already did it using only one query in OrderRepository by checking existing order and adding product to it, but that seems slow also.
Also, would it be better to use two separate transactions and rollback the first one if the second one (for products) fails?