我是一个胡言乱语的残骸,试图首先获取 EF 代码,让我做一些我可以在 SQL 中在 2 分钟内完成的事情。如果我还没有花 5 天时间尝试让它工作,我只会用 DDL 编写我的数据库并使用 ADO.NET。但是我离题了...
我想要 2 个表,其中 A 中的每条记录在 B 中都有一个相应的记录。它们都是同一对象的一部分;出于我不会讨论的原因,它们需要放在单独的表中(但它们确实如此,所以不要去那里(。如果我从数据库端设计它,我只会有一个从 B 到 A 的 FK 关系。
在 EF Code First 中,我尝试同时使用共享主键方法和一对一外键关联方法,但这两种方法都不适合我。我还尝试了我能想到的所有变体的 100 种左右组合,我没有进一步前进。
正如我所说,我想要的只是从 A 到 B 有一个可导航的关系(回来会很好,但我读过这是不可能的(,并且这种关系是懒惰加载的,这样我就可以说a.b
并可以访问 b 的字段。
我不可能列举我尝试过的所有事情,所以让我举一个例子来说明几乎有效的方法:
class Foo
{
public int Id { get; set; }
public string FooProperty { get; set; }
public virtual Bar Bar { get; set; }
}
class Bar
{
public int Id { get; set; }
public string BarProperty { get; set; }
}
请注意,没有从 Bar 到 Foo 的反向引用,因为 (a( SQL Server 会抱怨多个级联删除路径,(b( EF 会抱怨不知道哪一端是关联的主端。 所以... 很好 - 我可以没有它。
这让我进入数据库的是一个包含 Id
、FooProperty
和 Bar_Id
字段的Foos
表,以及一个包含 Id
和BarProperty
字段的Bars
表。这与我在SQL中建模的方式非常接近,尽管我可能会将FK字段放在Bar
而不是Foo
中。但既然是1:1,我想这并不重要。
我说这几乎有效的原因是,如果我添加一个Bar
和关联的Foo
,然后将它们加载回去,则Foo
对象的Bar
属性为 null。
using (var dbContext = new MyDbContext())
{
var foo = dbContext.Foos.Create();
foo.FooProperty = "Hello";
dbContext.Foos.Add(foo);
var bar = dbContext.Bars.Create();
bar.BarProperty = "world";
foo.Bar = bar;
dbContext.SaveChanges();
}
using (var dbContext = new MyDbContext())
{
foreach (var foo in dbContext.Foos)
Console.WriteLine(foo.Bar.Id); // BOOM! foo.Bar is null
}
我通常希望对foo.Bar
的评估会触发Bar
对象的延迟加载,但事实并非如此 - 该属性仍然null
。
我该如何解决它?
Thsi应该可以工作:
上下文
public class FoobarCtx : DbContext
{
public DbSet<Bar> Bars { get; set; }
public DbSet<Foo> Foos { get; set; }
public FoobarCtx()
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bar>()
.HasRequired(f => f.Foo)
.WithRequiredDependent(b => b.Bar)
.Map(x => x.MapKey("FooId"))
.WillCascadeOnDelete(true);
}
}
实体
public class Foo
{
public int Id { get; set; }
public string Foo1 { get; set; }
public string Foo2 { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public int Id { get; set; }
public string Bar1 { get; set; }
public string Bar2 { get; set; }
public virtual Foo Foo { get; set; }
}
我在 EF 4.3 中对其进行了测试,但我认为它也应该在 EF5 中工作。关键是 OnModelCreate 方法。在那里,您可以将其中一个定义为主体/后代,并摆脱Microsoft SQL 限制。
有关更多信息,请参阅此博客文章。有关模型生成器(流畅 API(的更多信息,请转到此处。
要启用延迟加载,请使用 DbContext.FooSet.Create(( 方法。这里的例子。
提醒自己,如果不出意外的话......
LueTm 找到了一个解决方案,它产生了我最初想到的表结构(在 Bar 表中有一个 FooId 列(,并在两个表上产生了独立的 PK。如果不先使用 dbContext.Foos.Include(f => f.Bar)
加载 Foo.Bar 属性,仍然无法访问该属性。
我还能够很好地使用共享主键(两个表都有一个PK,但只有Foos中的表是标识(自动增量(列,并且从条形中的Id到Foos中的Id存在FK关系。
为此,我在 Foo 类中有一个 Bar 属性,在 Bar 类中有一个 Foo 属性(因此双向导航有效(,并将以下内容放在我的 OnModelCreation 中。
modelBuilder.Entity<Bar>()
.HasRequired(x => x.Foo)
.WithRequiredDependent(x => x.Bar)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Foo>()
.HasRequired(x => x.Bar)
.WithRequiredPrincipal(x => x.Foo)
.WillCascadeOnDelete(true);
(我不确定这里的第二个调用是否真的做了什么(。
但同样,您仍然需要Include()
呼叫才能访问 foo.Bar
.
我自己刚刚经历了这个。我有一个 FoodEntry,有一个 Food,而 Food 有一个 FoodGroup。
public class FoodEntry
{
public int Id { get; set; }
public string User { get; set; }
public Food Food { get; set; }
public decimal PortionSize { get; set; }
public Portion Portion { get; set; }
public int Calories { get; set; }
public Meal Meal { get; set; }
public DateTime? TimeAte { get; set; }
public DateTime EntryDate { get; set; }
}
public class Food
{
public int Id { get; set; }
public string Name { get; set; }
public FoodGroup FoodGroup { get; set; }
}
public class FoodGroup
{
public int Id { get; set; }
public string Name { get; set; }
}
我首先使用代码,我的 POCO 类的定义就像你的一样。我没有标记为虚拟的属性。
无论如何,数据库都会如我所期望的那样生成 - 使用您所描述的外键。
但是此查询导致仅获取在 Food、Part 和 Meal 属性中包含空值的 FoodEntry 集合:
var foodEntries = db.FoodEntries
.Where(e => e.User == User.Identity.Name).ToList();
我将查询更改为此,并为我的集合加载了整个图形:
var foodEntries = db.FoodEntries
.Include( p => p.Food)
.Include(p => p.Food.FoodGroup)
.Include(p => p.Portion)
.Include( p => p.Meal)
.Where(e => e.User == User.Identity.Name).ToList()
;
对于 1:1 关系和 1:多关系(Foo 有几个柱线和一个 CurrentBar (:
modelBuilder.Entity<Foo>()
.HasOptional(f => f.CurrentBar)
.WithMany()
.HasForeignKey(f => f.CurrentBarId);
modelBuilder.Entity<Bar>()
.HasRequired(b => b.Foo)
.WithMany(f => f.Bars)
.HasForeignKey(b => b.FooId);