I am Business Logic component to enable Customers can place online orders. So far my simplified business logic look like this:
public class Product
{
public int productID { get; }
public string name { get; set; }
//other properties here like address and such
}
public class Order
{
public int orderID { get; }
public Customer customer { get; set; }
public List<Product> OrderItems { get; set; }
//other properties go here
}
List of Products will not support orders that contain products of multiple quantities. How do I add that support here? How would I call it from client side?
Another approach would be to add a level of indirection with an OrderItem class:
public class Product
{
public int productID { get; }
public string name { get; set; }
}
public class OrderItem
{
public Product product { get; set; }
public int quantity { get; set; }
}
public class Order
{
public int orderID { get; }
public Customer customer { get; set; }
public List<OrderItem> items { get; set; }
}
I.e. Order now refers to a list of OrderItems where each OrderItem has an associated quantity.
Don't use a List, use a Dictionary<Product,int>, where the int parameter is the quantity, or Dictionary<int,int>, where the first int is the product id and the second is the quantity.
You can always override .Equals for your Product class to be implemented in terms of your product id, so you're still using an int to define a product, but it may make things a bit simpler down the road (or if you ever need to change that).
I would add a third data object that contains order items that contains a link back to a product. The reason being is that you right now need quantity, but later I am going to guess you will want to give discounts on large where you might adjust the price per item down:
public class OrderLineItem
{
Product p { get; set; }
int Quantity {get; set;}
Decimal PricePerItem {get; set;}
}
You could make it something like
class OrderItem {
public Product Product ..
public int Qty ..
}
class Order {
public List<OrderItem> Items ..
}
You could structure it how you would envision a shopping cart to look. A single line would be a quantity of a certain product. Something like a ProductLine object which referenced a product and a quantity. Depending on how specific you logic is you may have additional attributes on a product such as manufacturer, SKU, etc. Sometimes you may get a comparable product from multiple manufacturers and for the sake of the order aren't interested but need to track that.
Please clarify:
1) In Class Order did you mean to write:
public List<Product> OrderItems() { get; set; }
//other properties go here
2) Are you sure you are not missing an intermediate object:
public class OrderItem
{
public int productID { get; }
public int quantity { get; set; }
// possibly other properties
}
In which case you would have:
public List<OrderItem> OrderItems() { get; set; }
3) Are you trying to ensure that each OrderItem has a quantity of One? In other words you do not want to allow people to order more than one of each product? Or are you trying to make sure that someone doesn't add the same product twice to the OrderItems?
Related
I'm currently developing an application on ASP.NET Core with Angular using Code First Migration and SQL Server. Now I have following "problem". I have data models with properties which always be refreshed on any change. The difficulty is that it is often calculated based on data of other models.
As an example:
I have these models (this is a little bit simplified):
public class Dinner {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Recipe> recipes {get; set; }
public Dinner ()
{
Recipes= new Collection<Recipe>();
}
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Ingredient> ingredients {get; set; }
public Recipe ()
{
Ingredients = new Collection<Ingredient>();
}
}
public class Ingredient {
public int Id { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
public Recipe Recipe { get; set; }
public int RecipeId { get; set; }
public decimal Quantity { get; set; }
}
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Price> Prices { get; set; }
public Product()
{
Prices = new Collection<Price>();
}
}
public class Price {
public int Id { get; set; }
public decimal PricePerUnit { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
}
I want to have
a calculated property for ingredient (which is the price for that specific quantity based on the price for the product)
a calculated property for recipe (the sum of all costs for all the ingredients)
a calculated property for dinner (the sum of all used recipes)
My question is: For best practice where should I do add this property?
Currently I calculate these properties on the app component by calculation the property of the used interface during the onInit() process. But this requires for example to load all the data up to prices to calculate the sum property of Dinner.
My goal is to have these sum property as up-to-date as possible but I would like to have the calculation (if possible) on SQL Server so I do need to load less data. Does this approach make sense? And how can I achieve that goal?
Looking at the model, it looks like you have three tables in your DB.
Ideally, you should keep these calculated values stored in DB.
This means that, when you are inserting a record for a dinner, you would add ingredients first, then calculate the total of all ingredients and insert the recipe. Likewise, calculate the total of all recipes and use the same while adding a Dinner. ALL THIS CALCULATION SHOULD HAPPEN IDEALLY INSIDE THE CONTROLLER(INSIDE REPOSITORY TO BE PRECISE).
Then, whenever you read the Dinner, you get the calculated values from the DB into your API.
What say?
You can add your calculations as calculated columns in SQL Server. The calculated columns would be marked as such in the EF Core model. When EF retrieves a Dinner, for example, the calculated dinner cost column will be computed in SQL Server and returned to EF Core without needing to retrieve the related tables.
I am making a school project which is a shop.
I have created a Product class:
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public string PhotoUrl { get; set; }
public int Quantity { get; set; }
and an Order class:
public Guid Id { get; set; }
public List<Product> Products { get; set; }
public decimal TotalPrice { get; set; }
//address
public string Street { get; set; }
public string HouseNumber { get; set; }
public string PostCode { get; set; }
public string City { get; set; }
public Order()
{
Products = new List<Product>();
}
As you see in Order.cs there is a list of Products, but entity framework always sets a relationship between my product and a order but I just want to add a Product to this list with no relation ship.
As a response I want to get something like this
{
"id" :"someID",
"products": [
{
first product
},
{
second product
}]
}
etc. How can I prevent creating by ef relationships and do simple lists?
Or how can I do a relationship many products to many orders?
You can add the NotMapped attribute to the Property:
...
[NotMapped]
public List<Product> Products { get; set; }
...
You will need to import System.ComponentModel.DataAnnotations
Either I'm missing something, or you are trying to do something that doesn't make much sense. You say there is no relation - but you do want to save both Order and it's Products to the database? That means that there IS relation (of 1:N kind) and EF is right to create it. It can't work without it.
You didn't include full output that you expect, only the Order part with first_product and second_product placeholders. If the placeholders look like your Product class, just let EF create the relation and you are done. If you want them to look different in JSON (omit some properties for example), you should still let EF create the relation, and then write transformation from your Entity classes (Product, Order) to DTO classes (ProductDTO, and OrderDTO that has List<ProductDto>). Which is good practice anyway, even if Entity and DTO match 1:1, in real project it rarely stays that way for long.
We know in a Database Model, Order and Orderline are generally two separate tables. When modelling the Domain Driven Classes, should they be generally be 1 Denormalized Class or separate classes?
One Domain Class:
public class Order
{
public int OrderHeaderId { get; set; }
public int CustomerId { get; set; }
public int OrderLineNumber { get; set;}
public int ProductId { get; set; }
public int Quantity { get; set;
}
Separated like Database:
public class OrderHeader
{
public int OrderHeaderId { get; set; }
public int CustomerId { get; set; }
public int OrderLineNumber { get; set;}
public virtual ICollection<OrderLine> OrderLine{ get; set; }
}
public class OrderLine
{
public int OrderLineNumber { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set;
public virtual ICollection<OrderHeader> OrderHeader { get; set; }
}
DDD - Aggregate Root - Example Order and OrderLine
You should try to focus on the design and not worry too much about persistence from the get-go. In terms of the Order/OrderLine example it is a rather well-known structure and represents a couple of design elements that are interesting.
When performing object modelling you most certainly don't need to flatten anything unless it really has to. I take the order example slightly further and include any value objects that are only ever related to the aggregate root as nested classes in the root class, like this:
public class Order
{
public Guid Id { get; }
public Guid CustomerId { get; }
public DateTime DateRegistered { get; }
private readonly List<Item> _items = new List<Item>();
public Order(Guid id, Guid customerId, DateTime dateRegistered)
{
Id = id;
CustomerId = customerId;
DateRegistered = dateRegistered;
}
public IEnumerable<Item> GetItems() => _items.AsReadOnly();
public void AddItem(Guid productId, string description, decimal count, decimal unitPrice)
{
_items.Add(new Item(productId, description, count, unitPrice));
}
public class Item
{
// get-only properties
internal Item(Guid productId, string description, decimal count, decimal unitPrice)
{
}
}
}
There are variations but you should implement it in a way that you are comfortable with. I prefer not to use aggregate instances when adding related objects such as the Product since that would mean my repository would need to somehow get to a Product when hydrating the Order instance. One could have overloaded methods for AddItem where one takes the productId and the other a Product where the product.Id is used but I see little value in that.
The interesting thing about the Order->Item scenario is that the OrderItem table, in an entity-relationship model sense, is an associative entity (or link-table) between Order and Product. However, we are all quite comfortable when dealing with this relationship and "know" that the items are related closer to the order and we model it as such. The reason I mention this is that one runs in many such scenarios where the side you need to pick to create a one-to-many relationship is not quite a evident.
If you are aiming for an object-oriented design, you should not concern yourself too much with data. Objects should have behavior.
If the objects are supposed to have behavior, it becomes easier to decide what is an object and what isn't. Does the OrderLine have something to actually do? If not, then it shouldn't exist. "Doing" here means something that directly contributes to some business function, not storing and retrieving data.
Working in one project (Catering theme ) when I was designing the database I didn't take care about some thing , and now Is very hard to avoid some kine of errors(Circular error).
Suppose I have following scenario :
I have Meal object that should be composed from a list of semi-finished products (we will call it Product ) and list of simple Resources.
One Product is composed from a list of Resoruces and list of products.
So in real example this will look like this:
Meal: Pizza that contains list of Resoruces(cheese,dough) and list of Products : in our case will be just :Sauce.
Sauce will be composed from List of Resources(salt,Some cheese ,tomato Sauce) and a List of Products (in our case will be just one "Chopped tomatoes with salt")
So now I have following classes:
public class Resource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductToProduct
{
public int Id { get; set; }
public Product MainProduct { get; set; }
public Product Component { get; set; }
public double Quantity { get; set; }
}
public class ProductToResource
{
public int Id { get; set; }
public Product Product { get; set; }
public Resource Resource { get; set; }
public double Quantityt { get; set; }
}
public class Meal
{
public int Id { get; set; }
public string Name { get; set; }
public IList<MealToProduct> MealToProducts { get; set; }
public IList<MealToResource> MealToResources { get; set; }
}
public class MealToResource
{
public int Id { get; set; }
public Meal Meal { get; set; }
public Resource Resource { get; set; }
public double Quantity { get; set; }
}
public class MealToProduct
{
public Meal Meal { get; set; }
public Product Product { get; set; }
public double Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ProductToResource> ProdcutToResources { get; set; }
public IList<ProductToResource> ProductToProducts { get; set; }
}
My problem is in relation between Product to Product.
Suppose I will have Product1, Product2 , Product3 , Product4.
Product 1 will be composed from something and Prodcut2, Product4.
Product2 will be composed from something and Prodcut3.
Prodcut 3 will be composed from something and Prodcut4.
Prodcut 4 will be composed from something and Prodcut1 , in this case when I will try to calcualte Cost for Product1 , or Product 4 I will get an Circular error.
So my problem is in ProductToProduct table.
My question is how I must to design tables to avoid this kind of errors .
I AM VERY SORRY FOR MY EXPLICATION BUT IT IS VERY HARD TO EXPLAIN THIS PROBLEM.
PLEASE ASK ME IF SOMETHING IS UNCLEAR.
THANKS FOR YOUR ATTENTION.
Note:This is not so important for this case but I am working in ASP.Net mvc , orm is Fluent Nhibernate.
Here's an example of a function you could use to detect whether a parent-child relationship exists. I have assumed that the product relationships are described in a table called ProductLink, which has two foreign keys to Product: ParentProductId and ChildProductId.
This function uses a recursive query to determine the complete list of products which are children of the product denoted by the argument #ParentProductId, then does a simple test to see whether #ChildProductId appears in that list.
create function dbo.ProductRelationshipExists
(
#ParentProductId int,
#ChildProductId int
)
returns bit
as
begin
declare #ChildExists bit = 0;
with ProductChildCTE as
(
-- Base case: Get the parent's direct children.
select ChildProductId from ProductLink where ParentProductId = #ParentProductId
-- Recursive case: Get the children's children.
union all
select
ProductLink.ChildProductId
from
ProductChildCTE
inner join ProductLink on ProductChildCTE.ChildProductId = ProductLink.ParentProductId
)
select #ChildExists = 1 from ProductChildCTE where ChildProductId = #ChildProductId;
return #ChildExists;
end
When someone tries to insert a record into ProductLink, you could use a test like this to determine whether the proposed parent and child are already in the table as child and parent, respectively, and disallow the insertion if so.
This was just a quick write-up to illustrate one possible approach; I should mention that I don't know how well the performance of this thing will scale as the table gets larger. Hopefully it will suffice for your case. If not, let me know how to improve it.
I have a MVC4 application where employees can order lunch. The shoppinglist is then available for the person who goes to the supermarket that day. An administrator can settle the costs made each month with a salary. The central order model looks like this:
public class Order
{
public int OrderId { get; set; }
public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public int UserId { get; set; }
public DateTime Date { get; set; }
public bool IsConfirmed { get; set; }
public bool IsSettledWithSalary { get; set; }
public virtual Product Product { get; set; }
public virtual User User { get; set; }
}
This table stores the orders for every product that is ordered. The order does have a quantity for when people order more than one product, but in general it becomes a long table of orders and products.
As can be seen there are two booleans in this table. An IsConfirmed and IsSettledWithSalary property. This is because people can make an order (IsConfirmed=false, IsSettledWithSalary=false), which can then be checked with what was available in the supermarket and the actual price (IsConfirmed=true, IsSettledWithSalary=false) and then finally the same orders can be put settled with salary by an admin (IsConfirmed=true, IsSettledWithSalary=true).
This last step now consists of adding a product called Transaction and setting the price to a negative value so that the balance of the user goes back to 0 and stores it in another Transaction table that looks like this:
public class Transaction
{
public int TransactionId { get; set; }
public DateTime Date { get; set; }
public List<Order> Orders { get; set; }
public decimal TransactionAmount { get; set; }
public virtual Order Order { get; set; }
}
That gives an overview.
I did start with the project with this idea in mind of storing negative Transactions products, but looking back I think there might be more elegant solutions.
A friend suggested that I should be using more than one table for orders. Or use a table that links more two different tables together.
I have no clear idea how to do this though and considering the fact that it is not slow as it is, and it works, I would love to hear your thoughts on the database structure as it is and receive advice on how to proceed.
Your current situation allows only one type of Product to be bought per order. It is highly unlikely that this is something you'd want. You should look at adding a table in between:
Order
Date
List<OrderDetails>
...
OrderDetails
OrderDetailsId
OrderId
ProductId
UnitPrice
Quantity
OrderToOrderDetails
OrderId
OrderDetailsId
In essence this would mean an extra 1-N table that will keep track of the products inside an order. A separate Transaction class might not even be necessary anymore since stuff like OrderDate, PaidDate, ReceivedDate can all be stored inside Order (or OrderDetails, should you wish to send orders separately).