Entity Framework: Mapping many-to-many - entity-framework

During my project in which I make some sort of webshop, I've came across a problem with my .NET backend where I use Entity Framework Code First with Fluent API.
In my frontend, Orders can be made and are passed to my backend where they end up as a Order object (code below). This Order contains a User and a Dictionary where Items and their ordered quantities are stored. My current goal is to store those Orders in my database to retrieve an Order history.
My understanding is that by itself, EF can't map a Dictionary. Being a student and having done mostly frontend, I don't really know how to tackle this.
I've tried converting that Dictionary to a List of ItemWrappers (containing both the Item and the amount) and making 2 tables: Order (OrderId, UserId) and OrderItem (OrderId, ItemId, Amount). This converts the Many-to-Many (Users to Items and Order is derived from the relation attribute) to a One-to-Many (Order to OrderItem).
I understand this from a purely database perspective. I could have managed if I were to write all the queries myself, but given that EF adds some abstraction to that, I am a bit lost. How do you suggest I do this?
The code is simplified to only show the class structure. Id is always generated on add and is used as primary key.
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class Order {
public IList<OrderItemWrapper> ItemsList { get; set; }
//Either one of these 2
public Dictionary<Item, int> Items { get; set; }
public User User { get; set; }
public int Id { get; set; }
}
public class OrderItemWrapper {
public Item Item { get; set; }//Will use ItemId as key
public int Amount { get; set; }
}

Could you please go through my explanation for many-to-many relationship here.
Is the following tree of SchoolContext correct?

Related

Specify a Parent-Child relationship in EF Core without using identity columns

Specify a Parent-Child relationship in EF Core without using identity columns
What's an efficient way within Entity Framework Core 5 (C#) to work with the data in a hierarchial table that is linked via non-identity columns.
Here's my primary class:
public class ServiceProvider
{
[Key]
public int Id { get; set; }
public string ParentSPCode { get; set; }
public string SPCode { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ContactEmail { get; set; }
public string Status { get; set; }
}
The SPCode value is unique, which I enforce via C# code. The ParentSPCode may be null or must match an existing SPCode. Again I enforce this via C# code.
I want this table to hold any number of levels of parent-child (1 or more) records, as defined by ParentSPCode-SPCode pairs.
I can retrieve these records via a complex hierarchy of LINQ "joins", but I am thinking there must be a cleaner way by defining the appropriate EF Core 5 relationship.
If I was in SQL Server, I would do this via a CTE.
I want to be able to bring in the child records in a manner similar to .Include(q => q.ParentSPCode == x.SPCode).

Entity framework one foreign key toward two tables - code first

All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.

EF 6 Code First storing an entity Reference to specific child in a one of the collections on the entity

I have a domain model that has a collection of entities configured in the normal 1 to many relationship, however I want to store a reference to a specific item in that collection using a FK in this model
The list as defined in the model
public ICollection<SLWOUpdate> Updates { get; set; }
The reference to the specific item in the list
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public virtual SLWOUpdate LastUpdate { get; set; }
Of course the code is responsible for updating the specific item as opposed to having EF do it.
Is this kind of relationship configurable in EF?
The reason I want to do this is for querying filtering purposes as part of complex query that must execute as one statement
Ended up adding a new domain model to represent the LastUpdate which simply holds a primary key to this entity and a FK to the LastUpdate
New Domain Model to represent the Last Update
public virtual SLCurrentWOUpdate LastUpdate { get; set; }
public class SLCurrentWOUpdate
{
[Key]
public int SLWorkOrder_Id { get; set; }
public SLWorkOrder SLWorkOrder { get; set; }
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public SLWOUpdate SLWOUpdate { get; set; }
}
I can query this as part of a larger more complex set of predicates... I just have to reach into the model one reference deeper:
db.SLWorkOrders
.Where(t => t.TAutoDeclined != null && t.TClosedPendingPayment != null)
.Where(t => t.LastUpdate.SLWOUpdate.UpdateStatusType.SystemName == "CHANGE_PRIORITY");
Feels kind of hackish.. but it works..

Entity Framework - Add an item to a list more than once

I am creating an Entity Framwork Code First app and am running into a problem when trying to add an entity to a list more than once.
I have the following two classes, which reference each other for a many-to-many relationship.
public class Order
{
public virtual List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public virtual List<Order> Orders{ get; set; }
}
This creates a the following three tables in my database:
Orders
- OrderId (PK, int)
.
OrderItems
- OrderItemId (PK, int)
.
OrderOrderItems
- Order_OrderId (PK,FK,int)
- OrderItem_OrderItemId (PK,FK,int)
In code, I wish to do the following:
private void AddOrderItemsTest
{
OrderItem orderItem = GetOrderItem(); // gets an existing order item from the DB
var order = new Order();
order.OrderItems.Add(orderItem);
order.OrderItems.Add(orderItem); // add the order item to the list a second time
context.Orders.Add(order);
}
When this gets persisted to the database, only a single orderItem entity is added to the list. We see in the table OrderOrderItems that OrderItem_OrderItemId is a PK and therefore must be unique. This means that EF has designed the tables in a way that won't allow more than one orderItem of the same type to be added to the list more than once.
Is there a Data Annotation that I can add to tell EF to allow me to add more than one item of the same type to the list?
I believe that it is not correct way of handling item count (of same item) in your order. Do you really want 10000s duplicated entries loaded into your OrderItems collection? I guess not.
You will need different primary key on OrderOrderItems table, hence suggest to introduce new entity that will contain OrderItem amount per Order:
public class OrderItemDetails
{
public int OrderItemDetailsId { get; set; }
public int OrderId { get; set; }
public int OrderItemId { get; set; }
public int Amount{ get; set; }
public virtual Order Order { get; set; }
public virtual OrderItem OrderItem { get; set; }
}
public class Order
{
public virtual List<OrderItemDetails> OrderItemDetails { get; set; }
}
public class OrderItem
{
public virtual List<OrderItemDetails> OrderItemDetails { get; set; }
}
And if you not happy with introducing Amount and still want to have duplicated entries per each item instance that will be absolutely fine because primary key of your many to many relation will be not combination of OrderId and OrderItemId but OrderItemDetailsId.

Entity Framework POCO Does Not Fit Nicely with Domain Objects

I have taken a model first approach for a project i'm working on. An example of a class relationship is shown as follows, pretty strightforward:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
}
The database schema will roughly be:
--------------
Products Table
--------------
Id int,
Name Varchar
------------
Photos Table
------------
Id int,
Path varchar
ProductId int FK Products.ID
A Product can have Zero or more Photos.
Now when i try to plug is my ORM of choice (Entity Framework V4 - Poco approach) iam forced to map my relationships in the domain model!
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
public int ProductId {get; set; } //Foriegn Key
public Product Proudct {get; set; } //For uni-directional navigation
}
Firstly, i dont need/want uni-directional navigation. I understand this can be deleted. Secondly, I dont want the Foriegn Key declared in the Photos class.
I dont think this is true POCO/persistence ignorance if i must define database properties in the Domain Objects?
Do other ORM's behave this way?
I found the answer. Using the wizard, there is an option to "Include foreign key columns in the model" - Uncheck this box and you will a clean conceptual model without FK.
Make sure Code Generation Strategy is set to none in the properties window.
Why don't you want to have Photo.Product property? If there is no such property, it seems one photo can belong to several products and since database schema should be more complex (with auxiliary table).
The relationships don't have to be two-way, and don't have to be public (if you use true POCOs, not proxy types). You've said quite a bit about what you don't want in your code, but can you be clearer about how you do want to define the relationships? It has to go somewhere. Where would you like to put it? There are many options.