Skip to content

Model & Fields

Mappings

This means mapping one class and its properties to the database table and its fields

Will be done automatically by EF

This will be done by the C# annotations

This will be done inside of the DbContext class and within the below method

protected override void OnModelCreating(ModelBuilder modelBuilder) {
...

Or we can create separate config classes for each model

public class AdvertismentConfig : IEntityTypeConfiguration<Advertisment> {
    public void Configure(EntityTypeBuilder<Advertisment> builder) {
        builder.Property(c => c.Price).HasConversion(c => c.Value.Value, d => Price.FromLong(d));
        builder.Property(c => c.OwnerId).HasConversion(c => c.Value.ToString(), d => UserId.FromString(d));
        builder.Property(c => c.ApprovedBy).HasConversion(c => c.Value.ToString(), d => UserId.FromString(d));
        builder.Property(c => c.Text).HasConversion(c => c.Value, d => AdvertismentText.FromString(d));
        builder.Property(c => c.Title).HasConversion(c => c.Value, d => AdvertismentTitle.FromString(d));
    }
}

We should add them inside the OnModelCreating method

modelBuilder.ApplyConfiguration(new FluentBookConfig());
modelBuilder.ApplyConfiguration(new FluentBookDetailsConfig());
modelBuilder.ApplyConfiguration(new FluentBookAuthorConfig());

Model

  • Convention: By default, EF will use the DbSet property name for the table name, for example in the below code EF will use Categories for table name

    public DbSet<Category> Categories { get; set; }
    
  • Data Annotation:

    [Table("tb_Category")]
    public class Category
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>().ToTable("tb_Category");
    }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<BookDetailsFromView>().HasNoKey().ToView("GetOnlyBookDetails");
    }
    

Fields

  • Convention: By default EF will use the Id field or <ModelName>Id field as primary key without need any annotation

    public class Category {
        public int Id { get; set; }
    }
    
  • Data Annotation: For the fields other than Id or <ModelName>Id, we can explicitly define our primary key like the below

    [Key]
    public int CategoryId { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>().HasKey(c => c.CategoryId);
    }
    

    Example 1: The below code will create a composite-key

    modelBuilder.Entity<BookAuthor>().HasKey(ba => new { ba.AuthorId, ba.BookId });
    

    Example 2: The below code will tell EF that this table doesn't have primary key

    modelBuilder.Entity<BookDetailsFromView>().HasNoKey();
    
  • Convention: EF will create FK and CategoryId field in the below code

    public class Book {
        public Category Category { get; set; }
    }
    
  • Data Annotation: If want to have a field name other than CategoryId, we can use the below code

    public class Book {
        [ForeignKey("Category")]
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
    
  • Convention:

    public class Book {
        public BookDetail BookDetail { get; set; }
    }
    
    public class BookDetail {
        public Book Book { get; set; }
    }
    
  • Data Annotation:

    public class Book {
        [ForeignKey("BookDetail")]
        public int BookDetailId { get; set; }
        public BookDetail BookDetail { get; set; }
    }
    
    public class BookDetail {
        public Book Book { get; set; }
    }
    
  • Fluent API:

    public class Book {
        public int BookId { get; set; }
        public int BookDetailId { get; set; }
        public BookDetail BookDetail { get; set; }
    }
    
    public class BookDetail {
        public Book Book { get; set; }
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Books>().HasKey(b => b.BookId);
        modelBuilder.Entity<Books>()
            .HasOne(z => z.BookDetail)
            .WithOne(z => z.Book)
            .HasForeignKey<Books>("BookDetailId");
    }
    
  • Convention:

    public class Book {
        public Publisher Publisher { get; set; }
    }
    
    public class Publisher {
        public List<Books> Books { get; set; }
    }
    

    Info

    Can be List<> or ICollection<>

  • Data Annotation:

    public class Book {
        [ForeignKey("Publisher")]
        public int PublisherId { get; set; }
        public Publisher Publisher { get; set; }
    }
    
    public class Publisher {
        public List<Books> Books { get; set; }
    }
    
  • Fluent API:

    public class Book
    {
        public int BookId { get; set; }
    
        public int PublisherId { get; set; }
        public Publisher Publisher { get; set; }
    }
    
    public class Publisher
    {
        public List<Books> Books { get; set; }
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Books>().HasKey(b => b.BookId);
        modelBuilder.Entity<Books>()
            .HasOne(z => z.Publisher)
            .WithMany(z => z.Books)
            .HasForeignKey(z => z.PublisherId);
    }
    
  • Convention:

    public class Book {
        public virtual ICollection<BookAuthor> Authors { get; set; }
    }
    
    public class Author {
        public virtual ICollection<BookAuthor> Books { get; set; }
    }
    
    // Intermediate table
    public class BookAuthor {
        public int BookId { get; set; }
        public Book Book { get; set; }
    
        public int AuthorId { get; set; }
        public Author Author { get; set; }
    }
    

    Info

    Can be ICollection<> or List<>

  • Fluent API:

    public class Book {
        public virtual ICollection<BookAuthor> Authors { get; set; }
    }
    
    public class Author {
        public virtual ICollection<BookAuthor> Books { get; set; }
    }
    
    // Intermediate table
    public class BookAuthor {
        public int BookId { get; set; }
        public Book Book { get; set; }
    
        public int AuthorId { get; set; }
        public Author Author { get; set; }
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<BookAuthor>().HasKey(b => new { b.BookId, b.AuthorId });
    
        modelBuilder.Entity<BookAuthor>()
            .HasOne(z => z.Book)
            .WithMany(z => z.Authors)
            .HasForeignKey(z => z.BookId);
    
        modelBuilder.Entity<BookAuthor>()
            .HasOne(z => z.Author)
            .WithMany(z => z.Books)
            .HasForeignKey(z => z.AuthorId);
    }
    
  • Data Annotation:

    [Column("BookISBN")]
    public string ISBN { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>()
            .Property(c => c.ISBN)
            .HasColumnName("bookISBN");
    }
    
  • Data Annotation:

    [Required]
    public string Title { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>()
            .Property(c => c.Title)
            .IsRequired();
    }
    
  • Data Annotation:

    [MaxLength(50)]
    public string Title { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>()
            .Property(c => c.Title)
            .HasMaxLength(50);
    }
    
  • Data Annotation:

    [NotMapped]
    public double DiscountedPrice { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<Category>()
                    .Ignore(c => c.DiscountedPrice);
    }
    

Identity: The Identity option specifies that the value will only be generated by the database when a value is first added to the database. Entity Framework Core will not implement a value generation strategy. Database providers differ in the way that values are automatically generated. E.g. Identity, GUID.

  • Data Annotation:

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public ... { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<...>()
                    .ValueGeneratedOnAdd(...);
    }
    

None: The None option prevents values from being generated by the database automatically in cases where they would otherwise be created.

  • Data Annotation:

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public ... { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<...>()
                    .ValueGeneratedNever(...);
    }
    

Computed: The Computed option specifies that the property's value will be generated by the database when the value is first saved, and subsequently regenerated every time the value is updated.

  • Data Annotation:

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public ... { get; set; }
    
  • Fluent API:

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<...>()
                    .ValueGeneratedOnAddOrUpdate(...);
    }