Почему ссылочные свойства работают только через контекст - PullRequest
7 голосов
/ 17 мая 2019

У меня есть два класса 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 };

Информация о пустом заказе и OrderId orderdetails также нулевые. Когда я добавляю созданную деталь заказа в контекст, тогда она будет добавлена ​​в Details, и OrderId становится идентификатором созданного заказа. Почему это работает только когда я добавляю это в контекст? Я хочу, чтобы он работал, не добавляя его в контекст. Может быть, мне нужно что-то делать в конструкторе классов (с параметром Context)? Как я могу это сделать?

EDIT: Классы Order и OrderDetails, унаследованные от абстрактного класса. 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;
}

Также, как вы видите, у меня есть конструктор без параметра. Я создал их, потому что 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 ]

3 голосов
/ 20 мая 2019

Я решил вторую проблему (A parameterless constructor was not found... исключение) следующим образом:

  1. Я установил конструктор по умолчанию для класса сущностей и дочерних сущностей как защищенный
  2. Когда я загружаю сущностьfrom DB Свойство Context для сущностей будет нулевым, поскольку 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();
    }
    }
    

И мой метод GetEntities в моем классе Context:

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

Может быть, были и лучшие способы, но я не мог их найти.Сейчас это работает, но я все еще жду хорошего ответа

1 голос
/ 19 мая 2019

Когда вы создаете объект OrderDetail, вы должны что-то с ним делать. В настоящее время вы установили только свойство Order. И это все, EntityFramework не знает, что этот объект существует, поэтому он ничего не может с ним сделать.

Чтобы сделать его «известным» EntityFramework, вы должны добавить его прямо или косвенно в контекст. Очевидный способ - добавить объект непосредственно в контекст, как вы уже сказали и попробовали:

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. Таким образом, он также сохранит объект OrderDetail.

...