【问题标题】:Entity Framework 6.1: CRUD on Child objects实体框架 6.1:子对象上的 CRUD
【发布时间】:2014-07-22 10:49:45
【问题描述】:

我使用来自存储库的对象列表填充数据网格,如下所示:

   public static IEnumerable<Order> GetOrdersForDataGrid()
    {
        IEnumerable<Order> query;
        using (RSDContext = new RSDContext())
        {
            query = context.Orders.Include(o=>o.OrderDetails).ToList();
        }
        return query;
    }

当我想编辑一个订单时,我将选定的行传递到一个新窗口,如下所示:

 OrderEditWindow orderEdit = new OrderEditWindow();
        orderEdit.SelectedOrder = SelectedOrder;
        orderEdit.ShowDialog();

这里我将Window的DataContext设置为:

DataContext = SelectedOrder;

在此窗口中,我有另一个数据网格绑定到OrderOrderDetails 集合属性。问题在于OrderDetails 上的 CRUD 操作。例如,在我像这样添加一个新的 orderDetail 之后:

  private void AddProductDetailButton_OnClick(object sender, RoutedEventArgs e)
    {
        if (!ValidateProductDetail())
            return;
        var _selectedProduct = ProductAutoCompleteBox.SelectedItem as Product;
        var selectedProduct = ProductsRepository.GetProductById(_selectedProduct.ProductId);
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.Price = selectedProduct.Price;
        orderDetail.ProductCode = selectedProduct.Code;
        orderDetail.ProductName = selectedProduct.Name;
        orderDetail.Quantity = int.Parse(QuantityNumericUpDown.Value.ToString());
        orderDetail.Um = selectedProduct.Um;
        orderDetail.Total = selectedProduct.Price * int.Parse(QuantityNumericUpDown.Value.ToString());
        orderDetail.Group = selectedProduct.Subgroup.Group.Name;
        orderDetail.Subgroup = selectedProduct.Subgroup.Name;
        orderDetail.SupplierName = selectedProduct.Supplier.Name;
        //orderDetail.Order=SelectedOrder;
        //orderDetail.OrderId = SelectedOrder.OrderId;
        SelectedOrder.OrderDetails.Add(orderDetail);

        ProductAutoCompleteBox.Text = string.Empty;
        QuantityNumericUpDown.Value = 1;
        ProductAutoCompleteBox.Focus();

    }

然后我从存储库调用更新方法:

  public static void UpdateOrder(Order order)
    {
        using (RSDContext context = new RSDContext())
        {
            context.Orders.Attach(order);
            context.Entry(order).State = EntityState.Modified;
            context.SaveChanges();
        }
    }

我收到关于 OrderId 的错误。如果我手动设置导航属性和 id,我不会收到错误,但更改不会保存到数据库中。

我的Order 模型如下所示:

 public  class Order : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Order()
    {
        _OrderDetails = new ObservableCollection<OrderDetail>();
        _OrderDetails.CollectionChanged += _OrderDetails_CollectionChanged;
    }

    void _OrderDetails_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            AttachProductChangedEventHandler(e.NewItems.Cast<OrderDetail>());
        if (e.OldItems != null)
            CalcualteTotals();
    }
    [NotMapped]
    public decimal CalculatedTotal
    {
        get
        {
            return OrderDetails.Sum(x => x.Total);
        }
    }
    public int OrderId { get; set; }

    private int _Number;

    public int Number
    {
        get { return _Number; }
        set
        {
            _Number = value;
            NotifyPropertyChanged("Number");
        }
    }

    private DateTime _Date;

    public DateTime Date
    {
        get { return _Date; }
        set
        {
            _Date = value;
            NotifyPropertyChanged("Date");
        }
    }

    private bool _Canceled;

    public bool Canceled
    {
        get { return _Canceled; }
        set
        {
            _Canceled = value;
            NotifyPropertyChanged("Canceled");
        }
    }
    private string _ClientName;

    public string ClientName
    {
        get { return _ClientName; }
        set
        {
            _ClientName = value;
            NotifyPropertyChanged("ClientName");
        }
    }
    private string _ClientPhone;

    public string ClientPhone
    {
        get { return _ClientPhone; }
        set
        {
            _ClientPhone = value;
            NotifyPropertyChanged("ClientPhone");
        }
    }

    private string _DeliveryAddress;

    public string DeliveryAddress
    {
        get { return _DeliveryAddress; }
        set
        {
            _DeliveryAddress = value;
            NotifyPropertyChanged("DeliveryAddress");
        }
    }

    private decimal _Transport;

    public decimal Transport
    {
        get { return _Transport; }
        set
        {
            _Transport = value;
            NotifyPropertyChanged("Transport");
        }
    }

    private decimal _Total;

    public decimal Total
    {
        get { return _Total; }
        set
        {
            _Total = value;
            NotifyPropertyChanged("Total");
        }
    }



    private ObservableCollection<OrderDetail> _OrderDetails;

    public virtual ObservableCollection<OrderDetail> OrderDetails
    {
        //get { return _OrderDetails ?? (_OrderDetails = new ObservableCollection<OrderDetail>()); }
        get
        {
            return _OrderDetails;
        }
        set
        {
            _OrderDetails = value;
            NotifyPropertyChanged("OrderDetails");
        }
    }
    private void AttachProductChangedEventHandler(IEnumerable<OrderDetail> orderDetails)
    {

        foreach (var p in orderDetails)
        {
            p.PropertyChanged += (sender, e) =>
            {
                switch (e.PropertyName)
                {
                    case "Quantity":
                    case "Price":
                    case "Total":
                        CalcualteTotals();
                        break;
                }
            };
        }

        CalcualteTotals();
    }

    public void CalcualteTotals()
    {
        NotifyPropertyChanged("CalculatedTotal");

    }
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this,
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

我的 OrderDetail 模型如下所示:

 public class OrderDetail : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;


    public int OrderDetailId { get; set; }
    public int OrderId { get; set; }

    public Order Order { get; set; }

    private int _ProductCode;
    public int ProductCode
    {
        get { return _ProductCode; }
        set
        {
            _ProductCode = value;
            NotifyPropertyChanged("ProductCode");
        }
    }

    private string _ProductName;

    public string ProductName
    {
        get { return _ProductName; }
        set
        {
            _ProductName = value;
            NotifyPropertyChanged("ProductName");
        }
    }

    private string _Um;

    public string Um
    {
        get { return _Um; }
        set
        {
            _Um = value;
            NotifyPropertyChanged("Um");
        }
    }

    private decimal _Price;

    public decimal Price
    {
        get { return _Price; }
        set
        {
            _Price = value;
            NotifyPropertyChanged("Price");
            NotifyPropertyChanged("Total");

        }
    }

    private int _Quantity;

    public int Quantity
    {
        get { return _Quantity; }
        set
        {
            _Quantity = value;
            NotifyPropertyChanged("Quantity");
            NotifyPropertyChanged("Total");

        }
    }

    private string _SupplierName;

    public string SupplierName
    {
        get { return _SupplierName; }
        set
        {
            _SupplierName = value;
            NotifyPropertyChanged("SupplierName");
        }
    }

    private string _Subgroup;

    public string Subgroup
    {
        get { return _Subgroup; }
        set
        {
            _Subgroup = value;
            NotifyPropertyChanged("Subgroup");
        }
    }

    private string _Group;

    public string Group
    {
        get { return _Group; }
        set
        {
            _Group = value;
            NotifyPropertyChanged("Group");
        }
    }

    public decimal _Total;

    public decimal Total
    {
        get { return Quantity * Price; }
        set
        {
            _Total = value;
            NotifyPropertyChanged("Total");
        }
    }

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this,
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

我真的在尝试使用某种工作单元,但我不明白我应该如何将 CRUD 应用于具有子集合的对象并同时保持 UI 更新(通过在 @ 987654332@ 并使用 Binding ClientPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged 我的父窗口在我输入时更新)

最终可行的解决方案:

   using (RSDContext context = new RSDContext())
        {
            var details = order.OrderDetails;
            order.OrderDetails = null;
            List<int> OriginalOrderDetailsIds =
             context.OrderDetails.Where(o => o.OrderId == order.OrderId).Select(o => o.OrderDetailId).ToList();
            List<int> CurrentOrderDetailsIds = details.Select(o => o.OrderDetailId).ToList();
            List<int> DeletedOrderDetailsIds = OriginalOrderDetailsIds.Except(CurrentOrderDetailsIds).ToList();

            context.Entry(order).State = EntityState.Modified;
            foreach (var deletedOrderDetailId in DeletedOrderDetailsIds)
            {
                context.Entry(context.OrderDetails.Single(o => o.OrderDetailId == deletedOrderDetailId)).State = EntityState.Deleted;
            }
            foreach (OrderDetail detail in details)
            {
                // Add.
                if (detail.OrderDetailId == 0)
                {
                    detail.OrderId = order.OrderId;
                    context.Entry(detail).State = EntityState.Added;
                }
                // Update.
                else
                {
                    context.Entry(detail).State = EntityState.Modified;
                }
            }
            context.SaveChanges();
        }

【问题讨论】:

  • 我认为问题不在 wpf 中,而在 EF 中,您需要从包含详细信息的上下文中获取订单,然后将订单 ui 与订单 db 合并
  • 如果我这样做,它可以工作,但我会失去 UI 实时更新。我知道我可以实现 pub/sub 模式(我必须先学习女巫),但我希望有一个解决方案,因为获取一个对象并修改它的所有方面然后将其推回数据库感觉很自然。
  • 我觉得push it back to the db子集合不是只通过改变父对象的状态来完成的,你需要合并来自ui和db的子集合
  • 我会研究如何做到这一点。谢谢。 *也许循环遍历所有孩子并手动插入/删除它们。我将在保存方法中尝试。我在想,如果我在 Collection Changed 事件中这样做,我会打破我的工作单位心态。

标签: c# wpf entity-framework binding


【解决方案1】:

您可以通过这种方式添加和更新孩子,但不确定用户界面中已删除的订单详细信息。如果您不想从实体中获取订单,则需要在 OrderDetail 中对已删除的 OrderDetail 进行某种标记。

using (RSDContext context = new RSDContext())
{
    var details = order.OrderDetails;
    order.OrderDetails = null;

    context.Entry(order).State = EntityState.Modified;
    foreach (var detail in details)
    {
        if (detail.Id == 0)
        {
            // Adds.
            detail.OrderId = order.Id;
            context.Entry(detail).State = EntityState.Added;
        }
        else if (detail.IsDeleted)
        // Adds new property called 'IsDeleted' 
        //  and add [NotMapped] attribute 
        //  then mark this property as true from the UI for deleted items.
        {
           // Deletes.
           context.Entry(detail).State = EntityState.Deleted;
        }
        else
        {
           // Updates.
           context.Entry(detail).State = EntityState.Modified;
        }
    }

    order.OrderDetails = details;
    context.SaveChanges();
}

【讨论】:

  • 是的,它有效。谢谢你。我认为这是最好的方法。我会研究如何知道实体已被删除
  • 我想我需要自己管理删除代码,因为现在我使用 Delete 命令,当我在这个循环中时,我看不到已删除的对象。谢谢你。 *我在想我可以与 db 中的对象进行比较并删除它们。
  • @andySF,您可以添加名为 IsDeleted 的新属性并添加 [NotMapped] 属性,然后在删除项目时从 UI 将其标记为 true(通过订阅集合更改事件或单击删除按钮事件?),作为我的更新答案
  • 我已经准备好了。我在集合更改事件上设置了 IsDeleted=true,但后来我意识到它没有用,因为在 foreach 循环中我无法遍历已删除的项目,因为它们不存在
  • 好吧,在这种情况下,这是有道理的,我认为来自 UI 的更改将通过某种Save 按钮应用,然后将已删除的项目存储在其他集合中(+ 将其标记为已删除),并在发送到后端之前重新添加到集合中
猜你喜欢
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-19
  • 1970-01-01
相关资源
最近更新 更多