Entity Framework 5 не будет вставлять самоссылающиеся отношения «многие ко многим»

Я не могу понять, почему эта конфигурация не будет генерировать оператор вставки в БД. Я проверяю через SQL Profiler.

Вот моя модель через Visio:

visiomodel

Вот мой edmx (сначала это база данных). Кружок показывает отношение самоссылки обратно к GeoBoundary.

edmxmodel

Вот мой код:

public void UpdateAssocs(Dictionary<int, List<int>> fromTo) {
  //iterate through each dictionary entry
  foreach (KeyValuePair<int, List<int>> entry in fromTo) {
    using (TransactionScope scope = new TransactionScope()) {
      //get a reference to the parent geoboundary for this entry
      GeoBoundary parent = contactContext.GeoBoundaries
        .FirstOrDefault(x => x.GeoID == entry.Key);
      //test to see if the parent is null, it shouldn't be b/c this dictionary was generated 
      // from a list of database values (but shit happens so throw an error if it is null)
      if (parent != null) {
        foreach (int childID in entry.Value) {
          //check to see if the child exists in the parents list of children
          GeoBoundary child = parent.GeoBoundaryAssocTo
            .FirstOrDefault(x => x.GeoID == childID);
          if (child == null) {
            //get a ref to the GeoBoundary that SHOULD be tied to the parent (it should exist but there just 
            // isn't an established relationship in the db)
            child = contactContext.GeoBoundaries
              .FirstOrDefault(x => x.GeoID == childID);
            //check the damn thing again b/c you never want to assume...
            // but if it's still null then do nothing!
            if (child != null) {
              parent.GeoBoundaryAssocTo.Add(child);                  
              contactContext.SaveChanges();
            }
          }
        }
      }
      else {
        throw new Exception(@"Parent GeoID passed to UpdateAssocs method or GeoID is null.");
      }
      scope.Complete();
    }
  }
}

Когда я добираюсь до parent.GeoBoundaryAssocTo.Add(child); в отладчике, я удостоверяюсь, что родитель и дочерний элемент существуют, затем я делаю шаг вперед, но ничего не получаю в профилировщике. Что дает? Проблема в том, что оба объекта уже существуют в базе данных, и я ничего не меняю, кроме отношений? Если да, то как я могу пометить отношение как измененное, чтобы EF сгенерировал вставку?

Детали EDMX:

    <AssociationSet Name="GeoBoundaryAssociation" Association="Contact.GeoBoundaryAssociation">
      <End Role="GeoBoundary" EntitySet="GeoBoundaries" />
      <End Role="GeoBoundary1" EntitySet="GeoBoundaries" />
    </AssociationSet>

    <Association Name="GeoBoundaryAssociation">
      <End Type="Contact.GeoBoundary" Role="GeoBoundary" Multiplicity="*" />
      <End Type="Contact.GeoBoundary" Role="GeoBoundary1" Multiplicity="*" />
    </Association>

person Chris    schedule 02.01.2013    source источник
comment
Почему TransactionScope?   -  person Scott Stafford    schedule 02.01.2013
comment
@ScottStafford - В случае, если что-то в Dictionary<int, List<int>> не так, я хочу, чтобы все это провалилось.   -  person Chris    schedule 02.01.2013
comment
Конечно, но он все равно сделает это, если вы не SaveChanges() для contactContext и вместо этого просто выбросите contactContext. Контекст - это единица работы - вам нужен TransactionScope только тогда, когда вы хотите иметь возможность откатиться ПОСЛЕ SaveChanges() или что-то в этом роде...   -  person Scott Stafford    schedule 02.01.2013
comment
Можете ли вы вставить соответствующий XML-файл .edmx, как ответчик вставляет сюда: stackoverflow.com/a/9760255/237091   -  person Scott Stafford    schedule 02.01.2013
comment
.edmx XML опубликован, дайте мне знать, если это неправильные части.   -  person Chris    schedule 02.01.2013


Ответы (2)


Вам нужно убедиться, что ваше свойство навигации подключено к текущему DBContext. В противном случае EF проигнорирует его.

попробуй это:

...

//get a reference to the parent geoboundary for this entry
GeoBoundary parent = contactContext.GeoBoundaries
    .include("GeoBoundaryAssocTo")
    .FirstOrDefault(x => x.GeoID == entry.Key);

...
// You may need to use include on your child entity too.
person Ben Tidman    schedule 02.01.2013
comment
Вы пытались также использовать свойство навигации для дочернего элемента? - person Ben Tidman; 02.01.2013
comment
Как насчет включения обоих концов многих ко многим на обоих, например: GeoBoundary parent = contactContext.GeoBoundaries .include(GeoBoundaryAssocTo).include(GeoBoundaryAssocFrom) .FirstOrDefault(x => x.GeoID == entry.Key); - person Ben Tidman; 02.01.2013
comment
Я тоже это пробовал. Я нахожу блоги, где другие добились успеха в этой работе, так что я не теряю надежды. - person Chris; 02.01.2013
comment
Что интересно, в отладчике я вижу, где счетчик коллекции GeoBoundaryAssocTo тикает на единицу, когда выполняется parent.GeoBoundaryAssocTo.Add(child);. Как будто EF просто не распознает, что он изменился. - person Chris; 02.01.2013

Что ж, я сам источник своих бед. Я не осознавал, что повторно использую свой contactContext (созданный как класс службы), и в более раннем вызове метода я установил AutoDetectChangesEnabled = false;. Все, что мне нужно было сделать, это снова включить отслеживание изменений. Почему-то мне никогда не приходило в голову, что класс обслуживания сохранил свое состояние от предыдущих вызовов. Живи и учись.

person Chris    schedule 03.01.2013