【问题标题】:Entity Framework one to many using fluent API实体框架一对多使用流畅的 API
【发布时间】:2016-10-28 21:51:49
【问题描述】:
public class Parent
{
    public int ParentId { get; set; }
    public string Name { get; set; }
}

public class Child
{
    public int ChildId { get; set; }
    public string Name { get; set; }
    public Parent Parent { get; set; }
    public int ParentId { get; set; }
}

一个父母可以有很多孩子。一个孩子可以有一个父母。 (是的,我知道,想不出更好的例子。)

您如何使用 fluent API 编写此代码?

请注意,Parent 没有List<Child> Children。这是故意的,也是问题的一部分。

【问题讨论】:

标签: c# entity-framework ef-code-first one-to-many ef-fluent-api


【解决方案1】:

这是一个完整的控制台应用程序,其中包含您需要的配置:

class Program
{
    static void Main(string[] args)
    {
        var ctx = new TesteContext();
        ctx.Database.CreateIfNotExists();

        Console.ReadKey();
    }
}

public class Parent
{
    public int ParentId { get; set; }
    public string Name { get; set; }
}

public class Child
{
    public int ChildId { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public virtual Parent Parent { get; set; }
}

public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
    public ParentConfiguration()
    {
        HasKey(x => x.ParentId);
    }
}

public class ChildConfiguration : EntityTypeConfiguration<Child>
{
    public ChildConfiguration()
    {
        HasKey(x => x.ChildId);

        HasRequired(x => x.Parent)
            .WithMany()
            .HasForeignKey(x => x.ParentId)
            .WillCascadeOnDelete(false);
    }
}

public class TesteContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Childs { get; set; }

    public TesteContext() : base("Teste_123")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ParentConfiguration());
        modelBuilder.Configurations.Add(new ChildConfiguration());
    }
}

并添加connection string 给你app.config:(替换数据库等)

<connectionStrings>
  <add name="Teste_123" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Teste_123;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
</connectionStrings>

  • 有导航属性,没有EntityTypeConfiguration

只需运行代码:

class Program
{
    static void Main(string[] args)
    {
        var ctx = new TesteContext();
        ctx.Database.CreateIfNotExists();

        Console.ReadKey();
    }
}

public class Parent
{
    public int ParentId { get; set; }
    public string Name { get; set; }
}

public class Child
{
    public int ChildId { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public Parent Parent { get; set; }
}

public class TesteContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Childs { get; set; }

    public TesteContext() : base("Teste_123")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Child>()
            .HasRequired(x => x.Parent)
            .WithMany();
    }
}

  • 没有导航属性

您可以添加迁移:

enable-migrations

使用下面的代码:

public class Parent
{
    public int ParentId { get; set; }
    public string Name { get; set; }
}

public class Child
{
    public int ChildId { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
}

public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
    public ParentConfiguration()
    {
        HasKey(x => x.ParentId);
    }
}

public class ChildConfiguration : EntityTypeConfiguration<Child>
{
    public ChildConfiguration()
    {
        HasKey(x => x.ChildId);
    }
}

public class TesteContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Childs { get; set; }

    public TesteContext() : base("Teste_123")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ParentConfiguration());
        modelBuilder.Configurations.Add(new ChildConfiguration());
    }
}

如果您添加迁移:

Add-Migration FirstMigration

你会得到下面的代码:

public partial class FirstMigration : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Children",
            c => new
                {
                    ChildId = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    ParentId = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ChildId);

        CreateTable(
            "dbo.Parents",
            c => new
                {
                    ParentId = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                })
            .PrimaryKey(t => t.ParentId);
    }

    public override void Down()
    {
        DropTable("dbo.Parents");
        DropTable("dbo.Children");
    }
}

只需在 up 方法上手动添加:

AddForeignKey("dbo.Children", "ParentId", "dbo.Parents", "ParentId", cascadeDelete: false);

你会得到:

public partial class FirstMigration : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Children",
            c => new
                {
                    ChildId = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    ParentId = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ChildId);

        CreateTable(
            "dbo.Parents",
            c => new
                {
                    ParentId = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                })
            .PrimaryKey(t => t.ParentId);

        AddForeignKey("dbo.Children", "ParentId", "dbo.Parents", "ParentId", cascadeDelete: false);
    }

    public override void Down()
    {
        DropTable("dbo.Parents");
        DropTable("dbo.Children");
    }
}

现在,当你运行 update-database 时,你会得到你想要的:

【讨论】:

  • 我只需要 .WithMany() 部分,但完整示例需要 +1。将来可能会帮助其他人。我将创建一个新的答案,因为我想保持简短和简单。 EntityTypeConfiguration 很好,让事情保持整洁,但我现在在我的应用程序中不需要它,也许在未来的版本中。此外,应谨慎使用延迟加载(虚拟)。
  • 据我所知,如果没有至少一个导航属性,就无法映射对象之间的关系。您可以尝试手动执行迁移...
  • 我改变了答案,添加了一个迁移示例。希望它会有所帮助;)
  • 这些不是必需的:modelBuilder.Entity() .HasKey(x => x.ParentId); modelBuilder.Entity() .HasKey(x => x.ChildId); EF 会按照约定自动处理主键。
  • 这个也不需要:.HasForeignKey(x => x.ParentId) EF 按照惯例知道这一点。
【解决方案2】:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany();
}

【讨论】:

    【解决方案3】:

    这里:

    public partial class Parent {
        public virtual int ParentId 
        public virtual string Name
    }
    
    public class Child {
        public virtual int ChildId 
        public virtual string Name
        public virtual Parent Parent
    }
    

    在你的映射上,做:

    public partial class ChildMap : ClassMap<Child> {
            public ChildMap(){
                Table("[child]");
                LazyLoad();
                Id(x => x.ChildId ).GeneratedBy.Identity().Column("ChildId ");
                Map(x => x.Name).Column("Name");
                References(x => x.Parent).Column("ParentId").Not.Nullable();
            }
        }
    
    public partial class ParentMap : ClassMap<Parent> {
            public ParentMap(){
                Table("[parent]");
                LazyLoad();
                Id(x => x.ParentId).GeneratedBy.Identity().Column("ParentId");
                Map(x => x.Name).Column("Name");
            }
        }
    

    这种映射是在 nhibernate 中完成的,但它的映射与实体相同。

    编辑:另外,如果你想访问一个父级的所有子级,你应该将列表添加到父级实体,并在父级映射上使用 HasMany 映射它

    【讨论】:

    • 哇。没想到会有一个 NHibernate 示例作为答案。有趣的。与 EF 相比,该 API 看起来很灵活,但对我的口味来说代码有点太多了。
    • 您已经用 NHibernate 示例回答了一个 EF 问题,EF 的代码完全不同
    • 只是sintax不同,但逻辑完全一样。
    • 我刚刚在我的一个项目中放置了一个 nhibernate 示例,将其转换为 EF 似乎需要做很多工作。两者声明关系的逻辑很相似,只是sintax不同
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    • 2019-07-05
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 1970-01-01
    相关资源
    最近更新 更多