1
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; }
}

One parent can have many children. One child can have one parent. (Yeah, I know, couldn't come up with a better example.)

How do you write this using the fluent API?

Note that the Parent does not have a List<Child> Children. That is intentional and part of the problem.

PussInBoots
  • 11,028
  • 9
  • 52
  • 84

3 Answers3

1

Here is a complete console application with the configuration you need:

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());
    }
}

And add the connection string to you app.config: (Replacing the database, etc)

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

  • With navigation property and without EntityTypeConfiguration

Just run the code:

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();
    }
}

  • Without navigation property

You can add a migration:

enable-migrations

With the code below:

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());
    }
}

If you add a migration:

Add-Migration FirstMigration

You will get the code below:

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");
    }
}

Just add manually on the up method:

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

And you'll get:

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");
    }
}

Now, when you run update-database, you'll get what you want:

enter image description here

Rafael Marques
  • 1,501
  • 15
  • 23
  • I only needed the .WithMany() part but +1 for the complete example. Might help others in the future. I will create a new answer tho since I want to keep it short and simple. The EntityTypeConfiguration is nice, keeps things nice and tidy but I don't need it in my app at the moment, maybe in future versions. Also, lazy loading (virtual) should be used with caution. – PussInBoots Oct 27 '16 at 21:18
  • As far as I know, you can't map a relation between objects without at least one navigation property. You can try to do manually with migrations... – Rafael Marques Oct 28 '16 at 12:54
  • I changed the answer, adding an example with migrations. Hope it will help ;) – Rafael Marques Oct 28 '16 at 13:25
  • 1
    These are not needed: modelBuilder.Entity() .HasKey(x => x.ParentId); modelBuilder.Entity() .HasKey(x => x.ChildId); EF will take care of the primary key automatically by convention. – PussInBoots Oct 28 '16 at 13:33
  • This one's not needed either: .HasForeignKey(x => x.ParentId) EF knows this by convention. – PussInBoots Oct 28 '16 at 13:36
  • Also, you don't need to add cascadeDelete manually. .WillCascadeOnDelete(false) will take care of that. I believe you ran the Add-Migration command twice. And the second time you forgot the -force flag to update the migration c# code. – PussInBoots Oct 28 '16 at 13:40
  • You are right, but I usually like to make explicit what I want. I'll edit removing the unnecessary code :D – Rafael Marques Oct 28 '16 at 13:47
0
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany();
}
PussInBoots
  • 11,028
  • 9
  • 52
  • 84
-2

Here:

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
}

and on your mappings, do:

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");
        }
    }

This mapping is done in nhibernate, but it maps just the same as entity.

Edit: Also, if you want to access all childs of one parent, you should add the List to parent entity, and map it with HasMany on the parent mapping

Stormhashe
  • 704
  • 6
  • 16
  • Wow. Didn't expect an NHibernate example as an answer. Interesting. The API looks flexible but a little bit too much code for my taste compared to EF. – PussInBoots Oct 27 '16 at 20:49
  • You have answered an EF question with an NHibernate example, The code is quite different for EF – undefined Oct 27 '16 at 20:49
  • Just the sintax is different, but the logic is exact the same. – Stormhashe Oct 27 '16 at 20:50
  • I just had a nhibernate example lying around on one of my projects, seemed a lot of work to convert it to EF. The logic of declaring the relations on both of them are very similar, just the sintax is different – Stormhashe Oct 27 '16 at 20:51