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
Idfield or<ModelName>Idfield as primary key without need any annotationpublic 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(...); }
