如何在Entity Framework中插入/更新master-detail?

时间:2016-11-18 14:53:45

标签: c# asp.net entity-framework master-detail

我正在尝试使用Entity Framework制作一个主从式Web窗体,并在同一页面上执行插入和更新。我是EF的新手,所以我必须在这里犯很多错误。你能帮我指点一下在EF上执行插入/更新的最佳实践吗?我在这做错了什么?

在此代码中,“新建”模式运行良好,但“编辑”模式会出现此错误:“实体对象不能被多个IEntityChangeTracker实例引用”。

    OrdersEntities ordersEntities = new OrdersEntities();

    private Order myOrder
    {
        get { return (Order)Session["myOrder"]; }
        set { Session["myOrder"] = value; }
    }

    public DataTable dtOrderDetails
    {
        get { return (DataTable)ViewState["dtOrderDetails"]; }
        set { ViewState["dtOrderDetails"] = value; }
    }

    private string Mode
    {
        get { return (string)ViewState["mode"]; }
        set { ViewState["_modo"] = value; }
    }

    private void btnSaveOrder_Click(object sender, EventArgs e)
    {
        if (dtOrderDetails.Rows.Count > 0)
        {
            using (ordersEntities)
            {
                using (var contextTransaction = ordersEntities.Database.BeginTransaction())
                {
                    try
                    {
                        if (Mode == "New")
                        {
                            Order newOrder = new Order();
                            OrderDetails newOrderDetails;

                            int maxOrderNumber = ordersEntities.Order.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
                            maxOrderNumber++;

                            newOrder.OrderNumber = maxOrderNumber;
                            newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                            newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
                            newOrder.Status = 1;

                            ordersEntities.Orders.Add(newOrder);

                            foreach (DataRow dt in dtOrderDetails.Rows)
                            {
                                newOrderDetails = new OrderDetails();
                                newOrderDetails.OrderNumer = maxOrderNumber;
                                newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
                                newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);

                                ordersEntities.OrderDetails.Add(newOrderDetails);
                            }

                            ordersEntities.SaveChanges();
                            contextTransaction.Commit();

                            myOrder = newOrder;
                        }

                        if (Mode == "Edit")
                        {
                            Order editedOrder = myOrder;
                            OrderDetails editedOrderDetails;

                            editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                            editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);

                            ordersEntities.Order.Attach(editedOrder);
                            ordersEntities.Entry(editedOrder).State = System.Data.Entity.EntityState.Modified;

                            editedOrder.OrderDetails.Clear();

                            foreach (DataRow dt in dtOrderDetails.Rows)
                            {
                                editedOrderDetails = new OrderDetails();
                                editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
                                editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
                                editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);

                                ordersEntities.OrderDetails.Add(editedOrderDetails);
                            }

                            ordersEntities.SaveChanges();
                            contextTransaction.Commit();
                        }
                    }
                    catch (Exception ex)
                    {
                        contextTransaction.Rollback();
                    }
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:2)

以下是你应该如何处理它。

最好用这个简单的界面抽象DbContext:

public interface IDataRepository : IDisposable
{
    IDbSet<Order> Orders { get; set; }

    void Save();
}

当然,您IDataRepository的实施基于EntityFramework。请注意,您需要在web.config文件中包含dataRepositoryConnection连接字符串:

public class EfDataRepository : DbContext, IDataRepository
{
    public EfDataRepository() : base("dataRepositoryConnection")
    {
    }

    public IDbSet<Order> Orders { get; set; }

    public void Save()
    {
        this.SaveChanges();
    }
}

根据我的经验,您还需要一个工厂&#39;,它为您提供数据存储库的新实例。这使您成为&#39;所有者&#39;实例,你可以安全地处理它。请注意,与DataContext的交互应该是最小的 - 您可以完成Unity of Work并摆脱它。不要重复使用!您将在下面看到它。

public class DataRepositoryFactory<T> where T : IDataRepository
{
    private Type dataRepositoryImplementationType;

    public DataRepositoryFactory(T dataRepositoryImplementation)
    {
        if (dataRepositoryImplementation == null)
        {
            throw new ArgumentException("dataRepositoryImplementation");
        }

        this.dataRepositoryImplementationType = dataRepositoryImplementation.GetType();
    }

    public T Create()
    {
        return (T)Activator.CreateInstance(this.dataRepositoryImplementationType);
    }
}

在您的控制器(如果它是MVC应用程序)或Page后端(表单)中,最好使用Microsoft Unity来获取DataRepositoryFactory的实例。目前,手动构造也足够了。

IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);

此外,您不需要放置所有这些交易/提交内容。它对你来说应该是透明的。 EF隐含地支持它,你不必明确它。

// See, now you are the 'owner' of the dataRepository
using (var dataRepository = this.dataRepositoryFactory.Create())
{
    if (Mode == "New")
    {
        Order newOrder = new Order();

        // This doesn't make sense. Either generate a random order number (e.g. a Guid), or just use the Order.Id as an order number, although I don't recommend it.
        int maxOrderNumber = dataRepository.Orders.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
        maxOrderNumber++;

        newOrder.OrderNumber = maxOrderNumber;
        newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
        newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
        newOrder.Status = 1;

        dataRepository.Orders.Add(newOrder);

        foreach (DataRow dt in dtOrderDetails.Rows)
        {
            OrderDetails newOrderDetails = new OrderDetails();
            newOrderDetails.OrderNumer = maxOrderNumber;
            newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
            newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);

            newOrder.OrderDetails.Add(newOrderDetails);
        }

        myOrder = newOrder;
    }

    if (Mode == "Edit")
    {
        Order editedOrder = dataRepository.Orders.FirstOrDefault(o => o.Id == myOrder.Id);

        editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
        editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
        editedOrder.OrderDetails.Clear();

        foreach (DataRow dt in dtOrderDetails.Rows)
        {
            OrderDetails editedOrderDetails = new OrderDetails();
            editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
            editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
            editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);

            editedOrder.OrderDetails.Add(editedOrderDetails);
        }
    }

    dataRepository.Save();
}

另外,我很确定您在EF代码优先方法中错误地设置了OrderOrderDetails类之间的关系。

这是错的:

OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;

如果你在这里发帖,我可以帮你解决。