如果您查看下面的代码并尝试使用DataAnnotations,我的EF 4.1 Code First模型应该是相当自我解释的。
特许经营出售许多物品。 特许经营有很多商店可以出售这些物品。 商店可能会以商品的形式出售。
一个名为SoldOutItems的多对多表格允许商店销售多件商品,并且商品在很多商店售罄。
我认为我只是对[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);
}
}
答案 0 :(得分:2)
简单,您需要按以下方式启动两个集合:
[Table("Stores")]
public class Store
{
public Store()
{
UnavailableItems = new HashSet<Item>();
}
...
}
[Table("Items")]
public class Item
{
public Item()
{
StoresUnavailableAt = new HashSet<Store>();
}
...
}