I have two classes Order and OrderDetail:
public class Order : Entity
{
public Order(KitchenAppContext context) : base(context)
{
}
public Order() : base()
{
}
public DateTime Date { get; set; }
public Guid MenuId { get; set; }
public virtual Menu Menu { get; set; }
public bool IsClosed { get; set; }
public decimal Price { get; set; }
public virtual int PeopleCount { get { return Details.Count; } }
public virtual List<OrderDetail> Details { get; set; } = new List<OrderDetail>();
}
public class OrderDetail : Entity
{
public OrderDetail(KitchenAppContext context) : base(context)
{
}
public OrderDetail() : base()
{
}
public Guid UserId { get; set; }
public virtual User User { get; set; }
public virtual List<PaymentDetail> Payments { get; set; } = new List<PaymentDetail>();
public virtual Order Order { get; set; }
public Guid OrderId { get; set; }
}
They are mapped like this:
void OrderMapping(ModelBuilder builder)
{
var etBuilder = builder.Entity<Order>();
etBuilder.HasKey(m => new { m.Id });
etBuilder.HasOne(o => o.Menu).WithMany(a => a.Orders).HasForeignKey(f => f.MenuId);
etBuilder.HasMany(o => o.Details).WithOne(d => d.Order).HasForeignKey(f => f.OrderId);
}
void OrderDetailMapping(ModelBuilder builder)
{
var etBuilder = builder.Entity<OrderDetail>();
etBuilder.HasKey(m => new { m.Id });
etBuilder.HasOne(o => o.User).WithMany(u => u.Details).HasForeignKey(f => f.UserId);
etBuilder.HasOne(o => o.Order).WithMany(u => u.Details).HasForeignKey(f => f.OrderId);
etBuilder.HasMany(o => o.Payments).WithOne(d => d.OrderDetail).HasForeignKey(f => f.OrderDetailId);
}
When I create order and order details :
var order = new Order(Context);
Context.Orders.Add(order);
var oderDetail = new OrderDetail(Context) { Order = order };
Details of order empty there and OrderId of orderdetails is null also. When I add created order detail in context then it will be added to Details and OrderId becomes Id of created order. Why it works only when I add it to context? I want that it works without adding it to context. Maybe, I should do something in consctructor of classes (with Context parameter)? How can I do this?
EDIT: Order and OrderDetails classes inherited from abstact class Entity:
public abstract class Entity
{
Guid id;
public Guid Id
{
get
{
if (id == null || id == Guid.Empty)
{
id = Guid.NewGuid();
}
return id;
}
set
{
id = value;
}
}
public Entity(KitchenAppContext context)
{
Context = context;
}
public Entity()
{
}
public MainContext Context;
}
Also, as you see, I have constructor without parameter. I created them because EF shows this message when I'm getting entities from context:
System.InvalidOperationException: 'A parameterless constructor was not found on entity type 'Order'. In order to create an instance of 'Order' EF requires that a parameterless constructor be declared.'
How can I avoid this error without creating conscrtuctors without parameter?
I solved second issue (A parameterless constructor was not found...
exception) like this:
I setted default constructor of Entity class and sub entities as protected
When I load entity from DB Context property of entities will be null, because EF uses default constructor. That's why I created my own IQuerable collection. It sets Context property when it's not setted:
class IContextable<T> :
IQueryable<T>
where T : Entity
{
public IQueryable<T> SourceQuery { get; set; }
public KitchenAppContext Context { get; set; }
public IContextable(IQueryable<T> query, KitchenAppContext context)
{
SourceQuery = query;
Context = context;
}
public Type ElementType => SourceQuery.ElementType;
public Expression Expression => SourceQuery.Expression;
public IQueryProvider Provider => SourceQuery.Provider;
public IEnumerator<T> GetEnumerator()
{
foreach (var entity in SourceQuery)
{
if (entity.Context == null || entity.Context != Context)
entity.Context = Context;
yield return entity;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
And my GetEntities method in my Context class:
public IQueryable<T> GetEntities<T>() where T : Entity
{
IQueryable<T> query = Set<T>();
return new IContextable<T>(query, this);
}
Maybe, there was better ways, but I couldn't found them. It works now, but I'm still waiting for good answer
When you create the OrderDetail
object you have to do something with it. Currently you have only set the Order
property. And that's it, EntityFramework doesn't know that this object exists so it cannot do anything with it.
To make it "known" to EntityFramework you have to add it directly or indirectly to the context. The obvious way is to add the object directly to the context as you have already said and tried:
Context.OrderDetails.Add(orderDetail);
Another way is it to add the OrderDetail
object to your Order
objects Details
property/list. When the Order
object is added to the context and it references a OrderDetail
object, then saving the Order
object will also "see" the OrderDetail
object and save it as well. This way the OrderDetail
object is added indirectly to the context. The code might look like this:
var order = new Order(Context);
var orderDetail = new OrderDetail(Context)
{
Order = order // might not be needed
};
order.Details.Add(orderDetail);
Context.Orders.Add(order);
This will save the Order
object. Additionally it will check the references and links and will see the added OrderDetail
object. So it will save the OrderDetail
object as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With