0

我是一个胡言乱语的残骸,试图首先获取 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 会抱怨不知道哪一侧是关联的主体端。所以...很好 - 我可以没有它。

这让我进入数据库的是一个Foos带有和字段的表Id,以及一个带有和字段的表。这与我在 SQL 中建模的方式非常接近,尽管我可能会将 FK 字段放入而不是. 但由于它是 1:1,我猜这并不重要。FooPropertyBar_IdBarsIdBarPropertyBarFoo

我说这几乎可行的原因是,如果我添加 aBar和关联Foo然后将它们重新加载,Bar则对象的属性Foo为空。

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

我该如何解决?

4

4 回答 4

3

这应该工作:

语境

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 中工作。关键是OnModelCreating方法。在那里,您可以将一个或另一个定义为主体/后代,并摆脱 Microsoft SQL 限制。

有关更多信息,请参阅此博客文章。有关模型构建器(流式 API)的更多信息,请访问此处

要启用延迟加载,请使用 DbContext.FooSet.Create() 方法。这里的例子。

于 2012-08-06T21:04:19.180 回答
0

提醒自己,如果没有别的...

LueTm 得出了一个解决方案,该解决方案产生了我最初想到的表结构(在 Bar 表中有一个 FooId 列),以及两个表上的独立 PK。如果不首先使用dbContext.Foos.Include(f => f.Bar).

我还能够使用共享主键使事情很好地工作(两个表都有一个 PK,但 Foos 中只有一个是标识(自动增量)列,并且从 Bars 中的 Id 到 FK 关系Foos 中的 ID。

为此,我在 Foo 类中有一个 Bar 属性,在 Bar 类中有一个 Foo 属性(因此 2 向导航有效),并将以下内容放在我的 OnModelCreating 中。

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.

于 2012-08-06T22:33:02.530 回答
0

我自己刚刚经历了这个。我有一个 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、Portion 和 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()
            ;
于 2012-08-20T03:20:38.140 回答
0

对于 1:1 关系以及1:many 关系(Foo 有多个 Bars 和 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);
于 2013-12-11T14:42:32.030 回答