【问题标题】:EntityFramework Code First: Why does Lazy Loading of Many to Many Reference fail?EntityFramework Code First:为什么延迟加载多对多引用会失败?
【发布时间】:2011-08-23 22:23:34
【问题描述】:

如果您查看下面的代码并尝试尽可能使用 DataAnnotations,我的 EF 4.1 Code First 模型应该是相当不言自明的。

特许经营商销售许多商品。 特许经营有许多可以出售商品的商店。 商店的商品可能已售罄。

一个名为 SoldOutItems 的多对多表允许一个商店拥有许多已售罄的商品,并且一个商品在许多商店都已售罄。

问题:

与多对多表关联的导航属性不会像普通导航属性那样延迟加载。当我在数据库优先方法中使用 EntityFramework 时,可以使用与下面模型生成的完全相同的数据库。

我认为我只是在 [InverseProperty] DataAnnotations 上做错了,但我在网上搜索的所有内容都表明只要导航属性是虚拟的,它就应该是“可延迟加载的”。

在最底部,我提供了在控制台应用程序中重现问题的代码。

特许经营

[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

    }
}

解决方案

kwon 的回答确实解决了我遇到的直接问题。然而,对我来说更大的问题是,我似乎无法简单地使用 DataAnnotations 获得我想要的东西。所以我最终删除了我的连接表(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);
    }
}

【问题讨论】:

    标签: entity-framework entity-framework-4.1 ef-code-first lazy-loading data-annotations


    【解决方案1】:

    很简单,您需要按以下方式启动两个集合:

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

    【讨论】:

    • 感谢您的回复。但是我有两个问题:(1)调用 myStore.UnavailableItems.Add(item) 确实将一个项目添加到我的集合中,但似乎没有在 ctx.SoldOutItems 和 item.StoresUnavailableAt 中创建实体,但它不应该是空的.
    • 看起来可以让多对多映射正常工作,我不能只使用DataAnnotations,必须使用Fluent。不幸的是,在 fluent 创建多对多映射时,我无法关闭级联删除,这会导致“循环引用”错误。因此,我必须删除我不想删除的不同级联删除(如我更新的问题所示)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多