From 6d1955783c82dbcb2b2db587f1ce7d1053c8f0e6 Mon Sep 17 00:00:00 2001 From: sharpchen Date: Sat, 24 Aug 2024 23:53:23 +0800 Subject: [PATCH] test --- docs/about.md | 85 ++++++++++++++++++- .../docs/8. Mapping Default Values.md | 47 ++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 docs/document/Entity Framework Core/docs/8. Mapping Default Values.md diff --git a/docs/about.md b/docs/about.md index 2d9336fc..5dd8f691 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,4 +1,83 @@ -# About me +# Complex Modeling + +## Motivation + +Columns from database are flat, but sometimes we'll want to represent certain columns as a whole model to better describe them in code base. +In other words, we need higher abstraction upon the columns. + +## Complex property + +```cs +record class Person(string? FirstName, string? LastName); +public class Movie +{ + public int Identifier { get; set; } + public string? Title { get; set; } + public DateTime ReleaseDate { get; set; } + public string? Synopsis { get; set; } + public string? DirectorFirstName { get; set; } // [!code --] + public string? DirectorLastName { get; set; } // [!code --] + public Person? Director { get; set; } // [!code ++] +} +``` + +Entity framework core will spread properties of `Director` as column mappings for database, +which means we should have `Director_FirstName` and `Director_LastName` as columns in database. + +```cs +builder.ComplexProperty(movie => movie.Director); // spread the structured property +``` + +Certainly, we can override mappings if you don't like the convetion. + +```cs +builder.ComplexProperty(movie => movie.Director) + .Property(movie => movie.FirstName) // [!code highlight] + .HasColumnName("d_firstname"); // [!code highlight] +``` + +## Complex modeling across tables + + +```cs +// equivalent to `ComplexProperty` +builder.OwnsOne(movie => movie.Director); // each movie entity owns one director +``` + +Generally we should separate the columns about something obviously not primitive to the table itself into another table for preventing pollution. + +```cs +builder.OwnsOne(movie => movie.Director) + .ToTable("Directors") // move director to another table // [!code highlight] +``` + +Since director is owned by `Movie` entity, Entity framework core generates a foreign key in director table as reference for movie table(also a primary key). +But director table is not independent, if a row of movie is removed, the director row will also be removed(yes directors are not reused). + +:::info +It's not common solution we do in database design, it's just a showcase. +::: + +### OwnsMany + +One movie will have multiple casts. + +```cs +public class Movie +{ + public int Identifier { get; set; } + public string? Title { get; set; } + public DateTime ReleaseDate { get; set; } + public string? Synopsis { get; set; } + public Person? Director { get; set; } + public ICollection? Casts { get; set; } // [!code ++] +} +``` + +While one entity can own many other entites, we use `OwnsMany`. + +```cs +builder.OwnsMany(movie => movie.Casts) + .ToTable("Casts"); // [!code highlight] +``` -This is a personal blog for documenting my learning journey of various things(primarily programming). -All those records are organized as markdowns. diff --git a/docs/document/Entity Framework Core/docs/8. Mapping Default Values.md b/docs/document/Entity Framework Core/docs/8. Mapping Default Values.md new file mode 100644 index 00000000..d9d19088 --- /dev/null +++ b/docs/document/Entity Framework Core/docs/8. Mapping Default Values.md @@ -0,0 +1,47 @@ +# Mapping Default Values + +## Motivation + +You may want a certain value as default for a column or a expression to invoke on update, insert and so on. + +## Static default + +```cs +builder.Property(x => x.FullName) + .HasDefaultValue("John Smith") // all default value should be `John Smith` in database! +builder.Property(x => x.CreatedDate) + .HasDefaultValue(DateTime.Now); // should be the datetime value when compiled. +``` + +## Dynamic default + +### Server side + +Once again, entity framework core translates the form, so we'll need a sql expression to map the dynamic default. + +```cs +builder.Property(x => x.CreatedDate) + .HasDefaultValueSql("now()"); // evaluates function `now()` on insert +``` + +### Client side + +Default values can also be generated dynamically from client side with a generator class. + +```cs +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ValueGeneration; + +public class CreatedDateGenerator : ValueGenerator +{ + public override DateTime Next(EntityEntry entry) + { + return DateTime.UtcNow; // what value should generate, can be more complex + } + + public override bool GeneratesTemporaryValues => false; +} + +builder.Property(x => x.CreatedDate) + .HasDefaultValue(); +```