为什么引用属性仅通过上下文起作用

时间:2019-05-17 04:47:54

标签: c# ef-core-2.0

我有两个类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也为null。当我在上下文中添加创建的订单明细时,它将被添加到Details中,并且OrderId成为创建订单的ID。为什么仅在将其添加到上下文时才起作用?我希望它能在不将其添加到上下文的情况下工作。也许,我应该在类的构造函数中做一些事情(使用Context参数)?我该怎么办?

编辑: 从abstact类Entity继承的Order和OrderDetails类:

 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.'

如何在不创建没有参数的构造函数的情况下避免此错误?

2 个答案:

答案 0 :(得分:3)

我解决了第二个问题(A parameterless constructor was not found...异常),

  1. 我将Entity类和子实体的默认构造函数设置为受保护的
  2. 当我从数据库中加载实体时,实体的属性将为null,因为EF使用默认构造函数。这就是为什么我创建了自己的IQuerable集合的原因。未设置时设置Context属性:

    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();
    }
    }
    

还有我的Context类中的GetEntities方法:

public IQueryable<T> GetEntities<T>() where T : Entity
    {
        IQueryable<T> query = Set<T>();
        return new IContextable<T>(query, this);
    }

也许有更好的方法,但是我找不到它们。现在可以使用,但我仍在等待好的答案

答案 1 :(得分:1)

创建import pandas as pd # Current data df = pd.DataFrame(data={'ID_Code':['AA_01', 'AA_01', 'BB_02', 'CC_03', 'CC_03', 'CC_03', 'CC_03', 'DD_05', 'DD_05'], 'Time_since_A':[0, 134, 0, 7, 145, 289, 312, 500, 650]}) print(df) # Intended output df2 = pd.DataFrame(data={'ID_Code':['AA_01', 'AA_01', 'BB_02', 'CC_03', 'CC_03', 'CC_03', 'CC_03', 'DD_05', 'DD_05'], 'Time_since_A':[0, 134, 0, 7, 145, 289, 312, 500, 650], 'Timepoint':[1, 2, 1, 1, 2, 3, 4, 'NA', 'NA']}) print(df2) 对象时,您必须对其进行处理。当前,您仅设置了OrderDetail属性。就是这样,EntityFramework不知道该对象存在,因此它不能执行任何操作。

要使其成为EntityFramework的“已知”,您必须将其直接或间接添加到上下文中。显而易见的方法是,正如您已经说过并尝试过的那样,将对象直接添加到上下文中:

Order

另一种方法是将Context.OrderDetails.Add(orderDetail); 对象添加到OrderDetail对象Order属性/列表中。将Details对象添加到上下文中并引用一个Order对象时,保存OrderDetail对象也将“看到” Order对象并将其保存。这样,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对象。