【发布时间】: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