为什么引用属性只能通过上下文工作
Why reference properties works only through context
我有两个 classes Order 和 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; }
}
它们的映射如下:
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);
}
当我创建订单和订单详情时:
var order = new Order(Context);
Context.Orders.Add(order);
var oderDetail = new OrderDetail(Context) { Order = order };
那里的订单详情为空,orderdetails 的 OrderId 也为空。当我在上下文中添加创建的订单详细信息时,它将被添加到 Details 并且 OrderId 成为创建订单的 Id。为什么只有当我将它添加到上下文时它才有效?我希望它在不将其添加到上下文的情况下工作。也许,我应该在 classes 的构造函数中做一些事情(使用 Context 参数)?我该怎么做?
编辑:
Order 和 OrderDetails classes 继承自抽象 class 实体:
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;
}
此外,如您所见,我有不带参数的构造函数。我创建它们是因为当我从上下文中获取实体时 EF 显示此消息:
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.'
如何在不创建不带参数的构造函数的情况下避免此错误?
当您创建 OrderDetail
对象时,您必须对其进行一些操作。目前你只设置了Order
属性。就是这样,EntityFramework 不知道这个对象的存在,所以它不能用它做任何事情。
要使其 "known" 变为 EntityFramework,您必须将其直接或间接添加到上下文中。显而易见的方法是将对象直接添加到上下文中,正如您已经说过并尝试过的那样:
Context.OrderDetails.Add(orderDetail);
另一种方法是将 OrderDetail
对象添加到您的 Order
对象 Details
property/list。当 Order
对象被添加到上下文中并且它引用了一个 OrderDetail
对象时,那么保存 Order
对象也会 "see" OrderDetail
对象并保存它以及。这样 OrderDetail
对象被 间接地 添加到上下文中。代码可能如下所示:
var order = new Order(Context);
var orderDetail = new OrderDetail(Context)
{
Order = order // might not be needed
};
order.Details.Add(orderDetail);
Context.Orders.Add(order);
这将保存 Order
对象。此外,它还会检查引用和链接,并会看到添加的 OrderDetail
对象。所以它也会保存 OrderDetail
对象。
我这样解决了第二个问题(A parameterless constructor was not found...
异常):
我将实体 class 和子实体的默认构造函数设置为受保护
当我从数据库上下文加载实体时 属性 实体将为空,因为 EF 使用默认构造函数。这就是我创建自己的 IQuerable 集合的原因。它在未设置时设置上下文 属性:
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();
}
}
我的 GetEntities 方法在我的上下文中 class:
public IQueryable<T> GetEntities<T>() where T : Entity
{
IQueryable<T> query = Set<T>();
return new IContextable<T>(query, this);
}
也许有更好的方法,但我找不到。现在可以用了,但我还在等待好的答案
我有两个 classes Order 和 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; }
}
它们的映射如下:
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);
}
当我创建订单和订单详情时:
var order = new Order(Context);
Context.Orders.Add(order);
var oderDetail = new OrderDetail(Context) { Order = order };
那里的订单详情为空,orderdetails 的 OrderId 也为空。当我在上下文中添加创建的订单详细信息时,它将被添加到 Details 并且 OrderId 成为创建订单的 Id。为什么只有当我将它添加到上下文时它才有效?我希望它在不将其添加到上下文的情况下工作。也许,我应该在 classes 的构造函数中做一些事情(使用 Context 参数)?我该怎么做?
编辑: Order 和 OrderDetails classes 继承自抽象 class 实体:
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;
}
此外,如您所见,我有不带参数的构造函数。我创建它们是因为当我从上下文中获取实体时 EF 显示此消息:
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.'
如何在不创建不带参数的构造函数的情况下避免此错误?
当您创建 OrderDetail
对象时,您必须对其进行一些操作。目前你只设置了Order
属性。就是这样,EntityFramework 不知道这个对象的存在,所以它不能用它做任何事情。
要使其 "known" 变为 EntityFramework,您必须将其直接或间接添加到上下文中。显而易见的方法是将对象直接添加到上下文中,正如您已经说过并尝试过的那样:
Context.OrderDetails.Add(orderDetail);
另一种方法是将 OrderDetail
对象添加到您的 Order
对象 Details
property/list。当 Order
对象被添加到上下文中并且它引用了一个 OrderDetail
对象时,那么保存 Order
对象也会 "see" OrderDetail
对象并保存它以及。这样 OrderDetail
对象被 间接地 添加到上下文中。代码可能如下所示:
var order = new Order(Context);
var orderDetail = new OrderDetail(Context)
{
Order = order // might not be needed
};
order.Details.Add(orderDetail);
Context.Orders.Add(order);
这将保存 Order
对象。此外,它还会检查引用和链接,并会看到添加的 OrderDetail
对象。所以它也会保存 OrderDetail
对象。
我这样解决了第二个问题(A parameterless constructor was not found...
异常):
我将实体 class 和子实体的默认构造函数设置为受保护
当我从数据库上下文加载实体时 属性 实体将为空,因为 EF 使用默认构造函数。这就是我创建自己的 IQuerable 集合的原因。它在未设置时设置上下文 属性:
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(); } }
我的 GetEntities 方法在我的上下文中 class:
public IQueryable<T> GetEntities<T>() where T : Entity
{
IQueryable<T> query = Set<T>();
return new IContextable<T>(query, this);
}
也许有更好的方法,但我找不到。现在可以用了,但我还在等待好的答案