【问题标题】:The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects无法定义两个对象之间的关系,因为它们附加到不同的 ObjectContext 对象
【发布时间】:2012-02-23 10:02:04
【问题描述】:

我在 SQL 中有 2 个表。一种称为Training,一种称为Consultants。培训是几个字段,例如ID, Place, Time, Date,它还有ConsultantName,它是外键并与Consultants 连接,有ConsultantName、ConsultantFirstName 等。

在保存训练期间,我首先保存训练,然后将训练值作为 varTraining 传递。我尝试根据我在堆栈上找到的一些建议将其附加到上下文中,但它似乎不起作用:

Training Training = context.Trainings.First(p => p.TrainingID == varTraining.TrainingID);
                context.Trainings.Attach(Training);
                currentUczestnik.Training = Training;

代码如下:

        using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM))
        {
            Training Training = context.Trainings.First(p => p.TrainingID == varTraining.TrainingID);
            foreach (var currentUczestnik in listaUczestnikow)
            {
                context.Trainings.Attach(Training);
                currentUczestnik.Training = Training;
                if (context.TrainingUczestnicies.Any(t => t.TrainingUczestnicy1 == currentUczestnik.TrainingUczestnicy1))
                {
                    context.TrainingUczestnicies.Attach(currentUczestnik);
                    context.ObjectStateManager.ChangeObjectState(currentUczestnik, EntityState.Modified);
                }
                else
                {
                    // context.Detach(currentUczestnik.Consultant);
                    context.Consultants.Attach(currentUczestnik.Consultant);
                    context.TrainingUczestnicies.AddObject(currentUczestnik);
                    //context.TrainingUczestnicies.Attach(
                }
            }
            try
            {
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                string excepion = ex.InnerException.ToString();
                MessageBox.Show(excepion);
            }

        }

编辑:

完整的故事是这样的。

这张桌子上有顾问。人们满载而归:

using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) {       
    IQueryable<Konsultanci> listaKonsultantow = from k in context.Konsultancis select k;      
    objectListViewKonsultanci.SetObjects(listaKonsultantow); 
}

它们被加载到 ObjectListview 中。然后,当用户单击该 ObjectListView 时,以下顾问将“复制”到 ObjectListView 参与者中。

        foreach (Konsultanci konsultant in objectListViewKonsultanci.SelectedObjects) {
            dodajUczestnikowSzkolen(konsultant);
        }

创建名为 SzkolenieUczestniczy (Participants) 的新对象,并在连接时为其分配顾问。还添加了更多属性。

    private void dodajUczestnikowSzkolen(Konsultanci konsultant) {
        SzkolenieUczestnicy nowyUczestnik = new SzkolenieUczestnicy();
        nowyUczestnik.Konsultanci = konsultant;

        //nowyUczestnik.SzkolenieUzytkownik = Settings.currentlyLoggedKonsultant.KonsultantNazwa;
        nowyUczestnik.SzkolenieUzytkownikData = DateTime.Now;
        //listaUczestnicy.Add(nowyUczestnik);
        objectListViewUczestnicy.AddObject(nowyUczestnik);
    }

现在用户保存培训(日期、时间、主题等一些字段已附加到 currentSzkolenie(培训):

    private void sqlZapiszSzkolenie() {

        using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) {
            if (context.Szkolenies.Any(t => t.SzkolenieID == currentSzkolenie.SzkolenieID)) {
                context.Szkolenies.Attach(currentSzkolenie);
                context.ObjectStateManager.ChangeObjectState(currentSzkolenie, EntityState.Modified);
            } else {
                context.Szkolenies.AddObject(currentSzkolenie);
            }


            context.SaveChanges();
            context.Detach(currentSzkolenie);

        }
    }

所以在我保存了 Training 并且 Training 现在在数据库中之后,我开始保存 Participants。我将currentSzkolenie(用于训练的全局变量)作为变量传递:

 private void sqlZapiszUczestnikow(ObjectListView listaUczestnikow, Szkolenie varSzkolenie) {


        foreach (SzkolenieUczestnicy currentUczestnik in listaUczestnikow.Objects) {
            using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) {
                  Szkolenie szkolenie = context.Szkolenies.First(p => p.SzkolenieID == varSzkolenie.SzkolenieID);

                  context.Attach(szkolenie);
                  currentUczestnik.Szkolenie = szkolenie;
              //  currentUczestnik.Szkolenie.EntityKey = szkolenie.EntityKey;
                if (context.SzkolenieUczestnicies.Any(t => t.SzkolenieUczestnicy1 == currentUczestnik.SzkolenieUczestnicy1)) {
                    context.SzkolenieUczestnicies.Attach(currentUczestnik);
                    context.ObjectStateManager.ChangeObjectState(currentUczestnik, EntityState.Modified);
                } else {
                    // context.Detach(currentUczestnik.Konsultanci);
                    context.Konsultancis.Attach(currentUczestnik.Konsultanci);
                    context.SzkolenieUczestnicies.AddObject(currentUczestnik);

                    //context.SzkolenieUczestnicies.Attach(
                }

                try {
                    context.SaveChanges();
                } catch (Exception ex) {
                    string excepion = ex.InnerException.ToString();
                    MessageBox.Show(excepion);
                }

            }
        }
    }

就是这样......它在currentUczestnik.Szkolenie = szkolenie;上抛出错误

【问题讨论】:

  • 读取异常我的猜测是listaUczestnikow 中的对象已经附加到另一个上下文。那可能吗?你从哪里得到这个列表?
  • currentUczestnik.Training = Training; 出现错误而不是在 listaUczestnikow 上。 ListaUczestnikow 是一个简单的 List,除了保留已经在数据库中的顾问之外,没有任何其他连接。这些顾问在数据库中,我希望他们被分配到培训。
  • 是的,但是currentUczestnik 是列表中的一个对象,这就是我的意思。 Consultants 已经在数据库中???您是否使用尚未处理的另一个上下文从数据库中加载它们?
  • 是的,我将它们从数据库加载到 ObjectListView 并且没有 .Detach 上下文。也许就是这样。但是当顾问被放入 ObjectListView 时,我使用它不应该处理上下文吗?
  • 使用 (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) { IQueryable listaKonsultantow = from k in context.Konsultancis select k; objectListViewKonsultanci.SetObjects(listaKonsultantow); }

标签: c# winforms entity-framework


【解决方案1】:

我用 2 个解决方案解决了这个问题。

  1. 我已经在我的班级顶部声明了private EntityBazaCRM context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM);,并且一直重复使用它,而没有将它放入using。多亏了这个动作,我不必在保存或任何东西之前附加任何东西。我只是使用相同的上下文并使用currentUczestnik.Szkolenie = szkolenie; 和 currentUczestnik.Konsultanci = advisors; 附加我想要的任何外键。它保存没有问题。

  2. 我没有像 currentUczestnik.Szkolenie = szkolenie; 这样附加整个对象,而是通过 SzkolenieReference 和 SzkolenieID 上的 EntityKey 添加它。

    context.Attach(szkolenie);
    context.Szkolenies.Attach(szkolenie);
    currentUczestnik.SzkolenieReference.EntityKey = szkolenie.EntityKey;
    currentUczestnik.SzkolenieID = szkolenie.SzkolenieID;
    
  3. 解决方案 3 是基于旧对象创建新对象。引用的 Konsultanci 和 Szkolenia 有点重新同步(我认为它实际上是从 db 中重新获取它们)。

     using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) {
                    if (currentUczestnik.SzkolenieUczestnicyID == 0) {
                        var nowy = new SzkolenieUczestnicy {
                            Konsultanci = context.Konsultancis.First(p => p.KonsultantNazwa == currentUczestnik.Konsultanci.KonsultantNazwa),
                            Szkolenie = context.Szkolenies.First(p => p.SzkolenieID == varSzkolenie.SzkolenieID),
                            SzkolenieUzytkownik = currentUczestnik.SzkolenieUzytkownik,
                            SzkolenieUzytkownikData = currentUczestnik.SzkolenieUzytkownikData,
                            UczestnikPotwierdzilUdzial = currentUczestnik.UczestnikPotwierdzilUdzial,
                            UczestnikPrzybyl = currentUczestnik.UczestnikPrzybyl
                        };
    
    
                        context.SzkolenieUczestnicies.AddObject(nowy);
                        context.SaveChanges();
                        listaDoPodmiany.Add(nowy);
    
  4. 解决方案 4 是我最终使用的。基本上它会将任何引用的对象置空(首先将它们临时保存到对象中),然后在保存之前只使用它们的 ID 值。然后在重新附加保存对象后。

            foreach (SzkolenieUczestnicy uczestnik in listaUczestnikow.Objects) {
                using (var context = new EntityBazaCRM(Settings.sqlDataConnectionDetailsCRM)) {
                    if (uczestnik.SzkolenieUczestnicyID == 0) { // add new object
                        var konsultant = uczestnik.Konsultanci; 
                        uczestnik.Konsultanci = null; // null attached object and reuse it's ID later on for SAVE purposes
                        uczestnik.KonsultantNazwa = konsultant.KonsultantNazwa;
                        uczestnik.Szkolenie = null; // null attached object and reuse it's ID later on for SAVE purposes
                        uczestnik.SzkolenieID = szkolenie.SzkolenieID;                       
                        context.SzkolenieUczestnicies.AddObject(uczestnik);
                        context.SaveChanges();
                        context.Detach(uczestnik); // detatch to prevent Context problems
                        uczestnik.Szkolenie = szkolenie;// reassign for use in ObjectListView
                        uczestnik.Konsultanci = konsultant; // reassign for use in ObjectListView
                    } else { // modify exisinng object 
                        context.SzkolenieUczestnicies.Attach(uczestnik);
    
                       //context.ObjectStateManager.ChangeObjectState(uczestnik, EntityState.Modified);
                        context.SaveChanges();
                    }
                }
            }
    

【讨论】:

  • 我会投票支持解决方案 1。我认为在 Winforms 应用程序中拥有与窗口生命周期一致的上下文生命周期是有好处的,尤其是您可以充分利用附加对象的更改跟踪。跨度>
  • 问题是它能否在多线程等更复杂的环境中存活下来。或者什么时候会有 50 个对象等。
  • 我添加了另一个解决方案。这似乎工作得很好,并且可以有非全局上下文。只是我认为它正在从 db 重新获取 Konsultanci,Szkolenie 参考。
猜你喜欢
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多