2016-11-18 18 views
0

私はEntity Frameworkと連携し、同じページで挿入と更新を実行するマスターディテールWebフォームを作成しようとしています。私はEFで新しいので、ここで多くの間違いをしているに違いない。 EFで挿入/更新を実行するためのベストプラクティスを教えていただけますか?私はここで間違って何をしていますか?Entity Frameworkでマスターディテールを挿入/更新する方法は?

このコードでは、「新規」モードは正常に機能しますが、「編集」モードでは「エンティティオブジェクトは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(); 
        } 
       } 
      } 
     } 
    } 
+0

うわー、これはひどいです。 –

+0

私は知っている、どんな助け? –

+0

私は数分/時間であなたに戻ってきます。 –

答えて

1

ここでは、どのようにアプローチする必要があります。

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

、あなたもあなたのデータリポジトリの新しいインスタンスを与える「工場」を、必要とします。これにより、インスタンスの「所有者」になることができ、安全に処分することができます。 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アプリだったら)あなたがDataRepositoryFactoryのインスタンスを取得するには、Microsoftユニティを使用している場合、それが最善だろう。今のところ手動での建設で十分です。

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; 

あなたはここに投稿する場合、私はあなたのためにそれらを修正することができます。

+0

ありがとうHristo!それは私を助ける! –

関連する問題