Обновление записей в отношениях «многие ко многим» в Entity Framework

#c# #entity-framework

#c# #entity-framework

Вопрос:

В моей базе данных у меня есть две таблицы: Trucks и TruckTypes. У многих грузовиков может быть много типов грузовиков. Я создал свои модели с помощью конструктора базы данных, и промежуточная таблица (которая содержит только первичные ключи) была проигнорирована, что, как я понимаю, является поведением EF по умолчанию:

введите описание изображения здесь

Когда я проверяю модели, кажется, что у обеих теперь есть ICollection соответствующей модели, которая, похоже, работает. Проблема возникает, когда я пытаюсь добавить новые элементы или обновить существующие элементы.

На данный момент я обновляю свои записи следующим образом:

 using (var db = new Entities())
{
    var truckResult = from t in db.ant_Truck
                        where t.Id == model.Id
                        select t;

    var truck = truckResult.Single();

    truck.Name = model.Name;
    truck.ant_TruckType.Clear();
    foreach (var truckType in model.TruckTypes)
        truck.ant_TruckType.Add(new ant_TruckType()
        {
            Name = truckType.Name,
            Disabled = truckType.Disabled
        });
    truck.Disabled = model.Disabled;

    db.SaveChanges();
}
  

Я думаю, что понимаю проблему: новые ant_truckType модели добавляются как «новые», а не связываются с существующими записями в таблице «ant_truckType», но я все еще не уверен, как их связать.

Я попытался создать другой метод, который запрашивал таблицу и пытался вернуть ICollection<ant_truckType> , но это начинает выдавать мне другие ошибки при преобразовании в ICollection:

 public ICollection<ant_TruckType> GetDbTruckTypesForTruck(ant_Truck truck)
{
    using (var db = new Entities())
    {
        var result = from tt in db.ant_TruckType
                        where tt.ant_Truck.Contains(truck)
                        select tt;

        var truckTypes = result.ToList() as ICollection<ant_TruckType>;

        return truckTypes;
    }
}
  

Я не уверен, правильно ли я все делаю на данный момент, поскольку я так долго пытался понять, почему это не работает. Какой наиболее эффективный способ попытаться выполнить вышеизложенное?

Заранее спасибо.

Ответ №1:

Типы грузовиков — это справочный список, в котором у грузовика может быть много типов грузовиков. Я бы, вероятно, не стал добавлять коллекцию грузовиков внутри типов грузовиков, чтобы упростить ссылки. Это все еще «многие ко многим», но вместо:

 .HasMany(x => x.TruckTypes)
.WithMany(x => x.Trucks)
  

… с соответствующей конфигурацией таблицы ссылок это было бы упрощено до:

 .HasMany(x => x.TruckTypes)
.WithMany()
  

При обновлении типов грузовиков на грузовике вам понадобится набор всех типов грузовиков для ссылки, затем вы можете разделить его, чтобы определить, какие типы необходимо добавить или удалить:

В идеале вашей модели просто нужно отправить список идентификаторов типа грузовика, которые должны быть связаны с грузовиком. Если вы отправляете типы грузовиков, добавьте шаг, чтобы получить соответствующие идентификаторы, которые вы хотите оставить связанными с грузовиком.

 using (var db = new Entities())
{
    // Load our truck and eager load its TruckTypes.
    var truck = db.ant_Truck.Include(t => t.TruckTypes).Single(t => t.Id == model.Id);
    
    // TODO: Update truck fields ...
    
    // Load all truck types we will want to associate with this truck.
    // For smaller sets we can simply load add truck types.
    var truckTypes = db.ant_TruckType.Where(tt => model.TruckTypeIds.Contains(tt.Id)).ToList();
    
    var existingTruckTypeIds = truck.TruckTypes.Select(tt => tt.Id).ToList();
    var truckTypeIdsToRemove = existingTruckTypeIds.Except(model.TruckTypeIds);
    var truckTypeIdsToAdd = model.TruckTypeIds.Except(existingTruckTypeIds);

    var truckTypesToRemove = truck.TuckTypes.Where(tt => truckTypeIdsToRemove.Contains(tt.Id));
    var truckTypesToAdd = truckTypes.Where(tt => truckTypeIdsToAdd.Contains(tt.Id));

    foreach(var truckType in truckTypesToRemove)
        truck.TruckTypes.Remove(truckType);
    foreach(var truckType in truckTypesToAdd)
        truck.TruckTypes.Add(truckType);

    db.SaveChanges();
}
  

Мы загружаем грузовик и включаем его текущие типы грузовиков, затем загружаем ссылки на все типы грузовиков, которые мы хотим связать с грузовиком из контекста БД. Оттуда мы проводим простую инвентаризацию идентификаторов типа грузовика в нашей модели грузовика. Исходя из этого, мы можем использовать Except для определения идентификаторов типа грузовика, которые необходимо удалить из нашего грузовика и добавить в грузовик. Затем эти результирующие списки идентификаторов могут получать ссылки из типов наших грузовиков (для удаления) и из типов загруженных грузовиков (для добавления).

При двунаправленных ссылках вам могут потребоваться накладные Trucks расходы на просмотр соответствующей коллекции TruckType и удаление или добавление из нее текущей корзины. Когда дело доходит до двунаправленных ссылок, я рекомендую избегать их, если они действительно не нужны / полезны. Вы всегда можете фильтровать типы грузовиков по грузовикам, запрашивая критерии для грузовиков, а затем используя SelectMany для предоставления типов грузовиков.