3

I am attempting to use the EntityConfiguration interface which is new in EF Core 2.0.

Addendum Probably helpful to see the Message class

public class Message 
{
    public int MessageId { get; set; }

    [Required]
    public string MessageForUserId { get; set; }
    public virtual ApplicationUser MessageForUser { get; set; }

    [Required]
    public string MessageFromUserId { get; set; }

    public virtual ApplicationUser MessageFromUser { get; set; }

    [Required]
    public string MessageContent { get; set; }

    public bool MessageRead { get; set; }

    [Required]
    public DateTime MessageCreatedOn { get; set; }

    public DateTime? MessageReadOn { get; set; }
}

and the AppUser class

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
    public SubscriptionLevel SubscriptionLevel { get; set; }
    public NotificationType NotificationType { get; set; }
    public string AvatarUrl { get; set; }
    public ICollection<Message> UserMessages { get; set; }
    public ICollection<Message> SentMessages { get; set; }
    public ICollection<IdentityUserClaim<string>> Claims { get;  set; }
}

Here is how the entity is defined in my OnModelCreating method.

builder.Entity<Message>()
            .HasOne(x => x.MessageForUser)
            .WithMany(x => x.UserMessages)
            .HasForeignKey(x => x.MessageForUserId)
            .OnDelete(DeleteBehavior.Restrict);

builder.Entity<Message>()
            .HasOne(x => x.MessageFromUser)
            .WithMany(x => x.SentMessages)
            .HasForeignKey(x => x.MessageFromUserId)
            .OnDelete(DeleteBehavior.Cascade);

My IEntityTypeConfiguration

public class AppUserConfig : IEntityTypeConfiguration<ApplicationUser>
{
    public void Configure(EntityTypeBuilder<ApplicationUser> builder)
    {           
        builder.Property(u => u.FirstName).HasMaxLength(31);
        builder.Property(u => u.FirstName).IsRequired();

        builder.Property(u => u.LastName).HasMaxLength(55);
        builder.Property(u => u.LastName).IsRequired();

        builder.Property(u => u.DateOfBirth).HasColumnType("datetime");
        builder.Property(u => u.DateOfBirth).IsRequired();

        builder.Property(u => u.Gender).IsRequired();
    }
}

The EF 6 way had a HasForeignKey method off builder which is no longer available and I cant seem to find any way to add them.

Here is how the migration built the table from my OnModelCreating method. I'd like to put most of these items in my configuration class.

migrationBuilder.CreateIndex(
            name: "IX_Messages_MessageForUserId",
            schema: "AppContext",
            table: "Messages",
            column: "MessageForUserId");

migrationBuilder.CreateIndex(
            name: "IX_Messages_MessageFromUserId",
            schema: "AppContext",
            table: "Messages",
            column: "MessageFromUserId");

migrationBuilder.AddForeignKey(
            name: "FK_Messages_AspNetUsers_MessageForUserId",
            schema: "AppContext",
            table: "Messages",
            column: "MessageForUserId",
            principalSchema: "AppContext",
            principalTable: "AspNetUsers",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);

migrationBuilder.AddForeignKey(
            name: "FK_Messages_AspNetUsers_MessageFromUserId",
            schema: "AppContext",
            table: "Messages",
            column: "MessageFromUserId",
            principalSchema: "AppContext",
            principalTable: "AspNetUsers",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);

There still is a HasIndex method, but I'm not sure how to duplicate what the migration is producing in the lambda expression for HasIndex.

dinotom
  • 4,990
  • 16
  • 71
  • 139

1 Answers1

5

HasForeignKey() is still available in EF Core, just like you did in EF6. The misconception is, that it is not available on EntityTypeBuilder<T>, but on ReferenceNavigationBuilder<T,K>, which is the return type from WithMany().

public class PostConfiguration : IEntityTypeConfiguration<Post>
{
    public void Configure(EntityTypeBuilder<Post> builder)
    {
        builder.HasOne(y => y.Blog)
                .WithMany(x => x.Posts)
                .HasForeignKey(x => x.BlogForeignKey);
    }
}


public class ApplicationContext :DbContext
{
    public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.ApplyConfiguration(new PostConfiguration());
    }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogForeignKey { get; set; }
    public Blog Blog { get; set; }
}
Marco
  • 22,856
  • 9
  • 75
  • 124
  • @Marco...There is no WithMany, there is HasMany, but my scenario is a bit different, at least how EF determined it, from the original migration. See the post update where I show the classes. – dinotom Nov 15 '17 at 13:24
  • trying various approaches vis-a-vis your answer but not getting same migration code as original – dinotom Nov 15 '17 at 13:35
  • Nowhere in your configuration class do you specifiy your relationship. If you do not specify your relationship in your `AppUserconfig`, you will not get `HasForeingKey()`, or do I misinterpret your problem? Just moving your OnModelCreating configuration in your config class would solve all your problems. – Marco Nov 15 '17 at 13:50
  • `HasForeignKey` API is available once you call `HasMany` or `HasOne` in any subsequent chain. There are multiple types which has the method since, different types are denoting different levels of configuration of relationship. Like `ReferenceNavigationBuilder` is relationship which has been configured with single reference navigation and inverse navigation is not specified/unknown. – Smit Nov 16 '17 at 23:47