EntityFramework Code First: почему не выполняется отложенная загрузка ссылок «многие ко многим»? - PullRequest
3 голосов
/ 24 августа 2011

Мой код EF 4.1 Первая модель должна быть достаточно понятной, если вы посмотрите на код ниже и я пытаюсь использовать DataAnnotations, когда могу.

Франшиза продает много товаров.У франшизы есть много магазинов, которые могут продавать товары.Магазин может быть продан за товар.

Таблица «многие ко многим», называемая SoldOutItems, позволяет магазину иметь много распроданных товаров, а товар можно распродать во многих магазинах.

Проблема:

Свойства навигации, связанные с таблицей «многие ко многим», для меня не будут лениво загружаться, как обычное свойство навигации.Это было возможно сделать, когда я использовал EntityFramework в подходе Database сначала с той же базой данных, которая была сгенерирована моделью ниже.

Я предполагаю, что я просто делаю что-то не так с DataAnnotations [InverseProperty], но всеЯ ищу в Интернете только состояния, когда свойство навигации является виртуальным, оно должно быть «загружаемым с отложенной загрузкой».

В самом низу я предоставил код для воспроизведения проблемы в консольном приложении.

Франшизы

[Table("Franchises")]
public class Franchise
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public Int64 Id { get; set; }
    [Required, MaxLength(50)]
    public string Name { get; set; }

    [InverseProperty("Franchise")]
    public virtual ICollection<Store> Stores { get; set; }

    [InverseProperty("Franchise")]
    public virtual ICollection<Item> Items { get; set; }
}

Магазины

[Table("Stores")]
public class Store
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public Int64 Id { get; set; }
    public Int64 FranchiseId { get; set; }
    [Required, MaxLength(50)]
    public string Name { get; set; }

    [ForeignKey("FranchiseId")]
    public virtual Franchise Franchise { get; set; }

    public virtual ICollection<Item> UnavailableItems { get; set; }
}

Предметы

[Table("Items")]
public class Item
{
    [Key]
    public Int64 Id { get; set; }
    public Int64 FranchiseId { get; set; }
    [Required, MaxLength(50)]
    public string Name { get; set; }

    [ForeignKey("FranchiseId")]
    public virtual Franchise Franchise { get; set; }

    public virtual ICollection<Store> StoresUnavailableAt { get; set; }
}

SoldOutItems

[Table("SoldOutItems")]
public class SoldOutItem
{
    [Key, Column(Order = 0)]
    public Int64 StoreId { get; set; }
    [Key, Column(Order = 1)]
    public Int64 ItemId { get; set; }

    [InverseProperty("UnavailableItems")]
    [ForeignKey("StoreId")]
    public virtual Store Store { get; set; }

    [InverseProperty("StoresUnavailableAt")]
    [ForeignKey("ItemId")]
    public virtual Item Item { get; set; }
}

Контекст

public class RetailContext : DbContext
{
    public DbSet<Franchise> Franchises { get; set; }
    public DbSet<Store> Stores { get; set; }
    public DbSet<Item> Items { get; set; }
    public DbSet<SoldOutItem> SoldOutItems { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoldOutItem>().HasRequired(s => s.Store).WithMany().WillCascadeOnDelete(false);
        base.OnModelCreating(modelBuilder);
    }
}

Код для воспроизведения нулевого исключения, которое выдается:

static void Main(string[] args)
{
    RetailContext ctx = new RetailContext();
    ctx.Configuration.LazyLoadingEnabled = true;

    Franchise franchise = ctx.Franchises.Where(f => f.Id == 0).FirstOrDefault();
    if (franchise == null)
    {
        franchise = new Franchise() { Id = 0, Name = "My Franchise" };
        ctx.Franchises.Add(franchise);
        ctx.SaveChanges();
    }

    Item item = ctx.Items.Where(i => i.Name == "Item 1").FirstOrDefault();
    if (item == null)
    {
        item = new Item() { Name = "Item 1" };
        franchise.Items.Add(item);
    }

    Store myStore = ctx.Stores.Where(s => s.Id == 0).FirstOrDefault();
    if (myStore == null)
    {
        myStore = new Store() { Id = 1, Name = "My Store" };
        myStore.UnavailableItems.Add(item); // Exception: UnavailableItems is null

    }
}

Решение

Квон действительно устраняет проблему, с которой я столкнулся.Однако гораздо большая проблема для меня заключается в том, что мне кажется, что я не могу получить то, что хочу, просто используя DataAnnotations.Поэтому я в итоге удалил свою Junction Table (SoldOutItems) в пользу использования Fluent.К сожалению, это создает проблему циклических ссылок, поскольку я не могу сейчас удалить каскадные удаления из моей таблицы «многие ко многим».

Новый код после удаления моей таблицы «SoldOutItems»:

public class RetailContext : DbContext
{
    public DbSet<Franchise> Franchises { get; set; }
    public DbSet<Store> Stores { get; set; }
    public DbSet<Item> Items { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Store>().HasMany(s => s.UnavailableItems).WithMany(i => i.StoresUnavailableAt).Map(mc => mc.ToTable("SoldOutItems").MapLeftKey("StoreId").MapRightKey("ItemId"));
        //Unfortunately I have to remove the cascading delete here since I can't figure out how to do it on the table created from the line above.
        modelBuilder.Entity<Franchise>().HasMany(f => f.Stores).WithRequired(s => s.Franchise).WillCascadeOnDelete(false);
        base.OnModelCreating(modelBuilder);
    }
}

1 Ответ

2 голосов
/ 24 августа 2011

Простой, вам нужно инициировать обе коллекции следующим образом:

[Table("Stores")]
    public class Store
    {
        public Store()
        {
            UnavailableItems = new HashSet<Item>();
        }
        ...
     }


[Table("Items")]
    public class Item
    {
        public Item()
        {
            StoresUnavailableAt = new HashSet<Store>();
        }
        ...
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...