Model & Fields
Mappings
Description
This means mapping one class and its properties to the database table and its fields
Conventions
Will be done automatically by EF
Annotations
This will be done by the C# annotations
Fluent API
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
Table Name
-
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"); }
ToView
-
Fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BookDetailsFromView>().HasNoKey().ToView("GetOnlyBookDetails"); }
Fields
Primary Key
-
Convention: By default EF will use the
Id
field or<ModelName>Id
field 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();
ForeignKey
-
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; } }
One-to-One Relation
-
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"); }
One-to-Many Relation
-
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); }
Many-to-Many Relation
-
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); }
Column
-
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"); }
Required
-
Data Annotation:
[Required] public string Title { get; set; }
-
Fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Category>() .Property(c => c.Title) .IsRequired(); }
MaxLength
-
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); }
NotMapped
-
Data Annotation:
[NotMapped] public double DiscountedPrice { get; set; }
-
Fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Category>() .Ignore(c => c.DiscountedPrice); }
DatabaseGenerated
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(...); }