EF Core Many to Many Join Table с неработающими дополнительными свойствами

Я хочу иметь эту таблицу соединений, которая имеет 2 внешних ключа, но также добавляет некоторые другие свойства:

[Serializable]
public class GroceryItemGroceryStore
{
    [Key, Column(Order = 0), Required]
    public int GroceryItemId { get; set; }
    [Key, Column(Order = 1), Required]
    public int GroceryStoreId { get; set; }

    public virtual GroceryItem GroceryItem { get; set; }
    public virtual GroceryStore GroceryStore { get; set; }

    [Required]
    public int NotInEstablishmentCount { get; set; }
    [Required]
    public int InEstablishmentCount { get; set; }
    [Required]
    public double Price { get; set; }
}

Что объединяет эти сущности:

public class GroceryItem : VeganItem<GroceryItemTag, GroceryStore>
{
    public GroceryItem(string name, string brand, string description, string image)
    {
        this.Name = name;
        this.Brand = brand;
        this.Description = description;
        this.Image = image;
    }

    [Required]
    public string Name { get; set; }
    [Required]
    public string Brand { get; set; }
    [Required]
    public string Description { get; set; }
    public string Image { get; set; }
    public virtual ICollection<GroceryItemGroceryStore> GroceryItemGroceryStores { get; set; }
}

[Serializable]
public abstract class VeganItem<VeganItemTagType, EstablishmentType>
{
    public int Id { get; set; }
    [Required]
    public int IsNotVeganCount { get; set; }
    [Required]
    public int IsVeganCount { get; set; }
    [Required]
    public int RatingsCount { get; set; }
    [Required]
    public int Rating { get; set; }
    [Required]
    public List<VeganItemTagType> Tags { get; set; }
    //[Required]
    public List<EstablishmentType> Establishments { get; set; }
    [Required]
    public int CurrentRevisionId { get; set; }
}

и эти объекты:

[Serializable]
public class GroceryStore : Establishment<GroceryItem>
{
    public GroceryStore(string name, string placeId, string street, string suburb, string city, string streetNumber)
    {
        this.Name = name;
        this.PlaceId = placeId;
        this.Street = street;
        this.StreetNumber = streetNumber;
        this.City = city;
        this.Suburb = suburb;
    }
}

[Serializable]
public abstract class Establishment<VeganItemType>
{
    public int Id {get; set;}
    [Required]
    public string Name {get; set;}
    [Required]
    public string PlaceId {get; set;}
    [Required]
    public string Street {get; set;}
    public string Suburb {get; set;}
    public string City {get; set;}
    
    public List<VeganItemType> VeganItems {get; set;}
    public string StreetNumber {get; set;}
}

Поэтому я пытаюсь указать свою таблицу соединений в качестве таблицы для использования при определении моих отношений «многие ко многим» в контексте моей базы данных:


builder.Entity<GroceryItem>()
    .HasMany(p => p.Establishments)
    .WithMany(p => p.VeganItems)
    .UsingEntity(j => j.ToTable("GroceryItemGroceryStores"));   

  

Что-то не так, потому что я получаю эту ошибку при запуске dotnet ef migrations add InitialCreate:

Невозможно использовать таблицу GroceryItemGroceryStores для типа сущности GroceryItemGroceryStore, поскольку она используется для типа сущности GroceryItemGroceryStore (Dictionary ‹string, object›) и, возможно, других типов сущностей, но связи нет. Добавьте внешний ключ в «GroceryItemGroceryStore» в свойствах первичного ключа и укажите первичный ключ в другой типизированной сущности, сопоставленной с «GroceryItemGroceryStores».

Где я ошибаюсь?

РЕДАКТИРОВАТЬ - Проблема с вставкой данных
Все таблицы успешно созданы. Но GroceryItemGroceryStores таблица и GroceryStores таблица не получают никаких вставок после вставки GroceryItem, в которой есть учреждение.

Ниже приводится полезная нагрузка, отправляемая для добавления GroceryItem, который также должен добавлять GroceryStore и GroceryItemGroceryStore:

{
    "name": "yummy fofod",
    "brand": "goodfoobdgffe",
    "description": "dfsad",
    "establishments": [{
        "name": "Macdonalds",
        "street": "Beach Rd",
        "placeId": "fsadfsdfsdfsdfsadfsadfsdf"
    }],
    "tags": [
        {
            "name": "sdf",
            "id": "9",
            "iconCodePoint": 23145
        }
    ]
}

И это контроллер, который обрабатывает вышеуказанный запрос полезной нагрузки:

[HttpPost]
public async Task<ActionResult<GroceryItem>> PostGroceryItem(GroceryItem groceryItem)
{
    _context.GroceryItems.Add(groceryItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetGroceryItem", new { id = groceryItem.Id }, groceryItem);
}

person BeniaminoBaggins    schedule 12.03.2021    source источник
comment
Вы пытаетесь определить отношения «многие ко многим» между какими моделями / объектами?   -  person atiyar    schedule 13.03.2021
comment
GroceryItem и GroceryStore. В GroceryItemGroceryStore я обновил имена внешних ключей, которые, как мне кажется, не соответствовали ожиданиям EF Core. GroceryStoreId становится EstablishmentsId. GroceryItemId становится VeganItemsId. Если эти имена внешних ключей более правильные, я могу обновить их в самом верхнем объекте вопроса.   -  person BeniaminoBaggins    schedule 13.03.2021
comment
@atiyar И, очевидно, впоследствии GroceryStore того же объекта (GroceryItemGroceryStore) становится Establishment, а GroceryItem становится VeganItem   -  person BeniaminoBaggins    schedule 13.03.2021


Ответы (1)


Наличие следующих двух свойств -

public virtual GroceryItem GroceryItem { get; set; }
public virtual GroceryStore GroceryStore { get; set; }

в модели GroceryItemGroceryStore уже поставил GroceryItem и GroceryStore во взаимосвязь «многие ко многим» через объединяющуюся сущность GroceryItemGroceryStore.

Затем ваш код настройки отношения пытается создать новое отношение «многие ко многим» между ними, но пытается использовать одну и ту же объединяющуюся сущность. Вот в чем проблема, и именно об этом говорится в сообщении об ошибке.

Удалите код конфигурации, и он должен работать автоматически.

Если вы хотите настроить отношения вручную, измените их на -

builder.Entity<GroceryItemGroceryStore>()
    .HasOne(p => p.GroceryItem)
    .WithMany(p => p.GroceryItemGroceryStores)
    .HasForeignKey(p=> p.GroceryItemId);
    
builder.Entity<GroceryItemGroceryStore>()
    .HasOne(p => p.GroceryStore)
    .WithMany()
    .HasForeignKey(p=> p.GroceryStoreId);

Или еще лучше было бы добавить следующее свойство -

public virtual ICollection<GroceryItemGroceryStore> GroceryItemGroceryStores { get; set; }

к модели GroceryStore и изменив конфигурацию на -

builder.Entity<GroceryItemGroceryStore>()
    .HasOne(p => p.GroceryItem)
    .WithMany(p => p.GroceryItemGroceryStores)
    .HasForeignKey(p=> p.GroceryItemId);
    
builder.Entity<GroceryItemGroceryStore>()
    .HasOne(p => p.GroceryStore)
    .WithMany(p=> p.GroceryItemGroceryStores)
    .HasForeignKey(p=> p.GroceryStoreId);

РЕДАКТИРОВАТЬ:
Вам следует удалить свойство Establishments из модели VeganItem и свойство VeganItems из модели Establishment. В противном случае они создадут отдельную связь «многие ко многим» через отдельную таблицу соединения.

РЕДАКТИРОВАТЬ - для вставки данных
Выполните следующие действия:

  1. Создайте новый GroceryStore (например, newStore) из ваших полезных данных
  2. Вставьте его в базу данных
  3. Создайте новый GroceryItem (например, newItem) из полезной нагрузки
  4. Сделайте следующее -
newItem.GroceryItemGroceryStores = new List<GroceryItemGroceryStore>
{
    new GroceryItemGroceryStore { GroceryStoreId = newStore.Id }
};

_context.GroceryItems.Add(newItem);
await _context.SaveChangesAsync();
person atiyar    schedule 13.03.2021
comment
Ура, я добавил эту виртуальную коллекцию. В нижнем блоке кода должно быть WithMany(p => p.GroceryItemGroceryStores) что-то еще? Как выдает ошибку 'GroceryItemGroceryStore' does not contain a definition for 'GroceryItemGroceryStores' - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Извините, моя ошибка. Проверьте обновленный ответ. - person atiyar; 13.03.2021
comment
Спасибо. Кажется, создается 2 таблицы соединения: GroceryItemGroceryStore и GroceryItemGroceryStores, и вставляются только внешние ключи в GroceryItemGroceryStore и ничего не вставляется в GroceryItemGroceryStores - person BeniaminoBaggins; 13.03.2021
comment
Стоит отметить, что у меня есть это public DbSet<GroceryItemGroceryStore> GroceryItemGroceryStores { get; set; } - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Проверьте, пожалуйста, редактирование. - person atiyar; 13.03.2021
comment
Я обновил каждую часть своего вопроса с помощью моего текущего кода, потому что я изменил имена своих свойств, чтобы они были более универсальными во всем моем приложении, поэтому в любом учреждении есть VeganItems, а в любом veganItem - Establishments. Я считаю, что реализовал ваш ответ, просто изменив имена полей. Но теперь у меня есть таблица GroceryItemGroceryStores, которая великолепна, но в нее ничего не вставляется. - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Я думаю, вам следовало оставить все как есть и опубликовать новый вопрос с модификацией. Ответ, который я опубликовал, больше не имеет никакого смысла против вашего текущего кода. - person atiyar; 13.03.2021
comment
Изменились только два свойства. GroceryStore.GroceryItem ›GroceryStore.VeganItem и GroceryItem.GroceryStore› GroceryItem.Establishment - person BeniaminoBaggins; 13.03.2021
comment
Я могу отредактировать имена полей для ответа, если хотите - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Хорошо. Теперь я понимаю - изменения, которые вы внесли в именование, не повлияют на то, что я предложил в ответе, или в сгенерированных таблицах, и он все равно должен работать. Итак, что не работает? - person atiyar; 13.03.2021
comment
Сообщите мне, если вы хотите, чтобы я отредактировал имена свойств в вашем ответе в соответствии с моими новыми соглашениями об именах, и я также могу удалить большую часть разговора в конце, чтобы быть кратким. Я думаю, что применил ваш ответ к своему коду - который теперь опубликован в исходном вопросе - я сделал это только для того, чтобы вы могли видеть, что я сделал, поскольку у него все еще есть одна проблема - все таблицы хороши, но таблица GroceryItemGroceryStores нет получить какие-либо вставки после вставки groceryItem, на котором есть заведение. - person BeniaminoBaggins; 13.03.2021
comment
В таблице GroceryStores нет вставок. Однако таблица GroceryItems делает - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Это должно работать. Теперь это зависит от того, как вы это делаете. Я предлагаю вам опубликовать еще один вопрос с кодами, показывающими, как вы пытаетесь вставить. Что касается того, что я сказал о сохранении поста как есть - согласно вашему текущему редактированию, это сообщение об ошибке в вашем посте или то, что я упомянул о коде конфигурации, или о том, как настроить и т. Д., Не имеет никакого смысла :) Вы заметили что ? - person atiyar; 13.03.2021
comment
Да, я понимаю, что это проблема. Я вернул вопрос к вашему первому редактированию, чтобы все снова стало осмысленным, и добавил свои полезные данные запроса и связанный контроллер в нижнюю часть вопроса, который можно удалить после того, как вы его увидели. - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Создайте новый GroceryStore (например, newStore) из ваших полезных данных. Вставьте это. После того, как это вставлено, создайте новый GroceryItem (например, newItem) из полезной нагрузки. Тогда newItem.GroceryItemGroceryStores = new List<GroceryItemGroceryStore>{new GroceryItemGroceryStore{GroceryStoreId=newStore.Id}}; _context.GroceryItems.Add(newItem); await _context.SaveChangesAsync(); - person atiyar; 13.03.2021
comment
Как ни странно, это все еще не работает. XD Значение GroceryItemGroceryStore.EstablishmentId неизвестно при попытке сохранить изменения. Это связано с тем, что свойство также является частью внешнего ключа, для которого неизвестна основная сущность в отношении. Я определенно нахожусь на лучшем пути после вашего последнего комментария, хотя я не делал ничего из этого и определенно должен. - person BeniaminoBaggins; 13.03.2021
comment
@BeniaminoBaggins Скорее всего, GroceryStore не был вставлен. Вы должны сначала вставить его в базу данных с помощью вызова SaveChanges(). - person atiyar; 13.03.2021
comment
Это работает. Большое спасибо за помощь! Шутки в сторону. Было много усилий. Удалите все эти сообщения, если хотите, чтобы вопрос был удобнее для просмотра другими. Ваше здоровье! - person BeniaminoBaggins; 13.03.2021