HasConversion for mapping value objects (like a Vehicle Identification Number - VIN), HasComplexProperty for composite value objects (like Name), and discriminators for inheritance mapping.AsNoTracking, split queries) and for managing database migrations outside of application startup, recommending the EF migrations bundle tool for deployment.
public class Vehicle { public int Id { get; set; } ... } is a typical DTO with no logic or encapsulation.
try-catch blocks, as setters are typically assumed to just work.
Vehicles, VehicleOwners (coupling table with From and To dates), People (FirstName, LastName), and Addresses (with Type and IsCurrent columns).
Address.cs and Person.cs are shown with public get; set; for all properties, including [Required] and [StringLength] annotations directly on them, illustrating the typical DTO approach.PersonService demonstrates validation logic (e.g., checking for null/whitespace in address fields) and business rules (e.g., limiting to three addresses, setting IsCurrent) directly within service methods like SetAddress.
VehicleService shows similar scattering of business rules (e.g., setting To date for previous owner) in methods like SetCurrentOwner.
Person entity that takes a PersonDTO (the dumb EF object) in its constructor and exposes methods like SetDeliveryAddress or SetInvoiceAddress, which contain the business logic.
Vehicle Management (handling Vehicle and Owner) and People Management (handling Person and Address). This addresses the conceptual difference between an "owner" in the context of a vehicle and a "person" with an address.
Table Splitting and Multiple DbContexts as ways to map different entities to the same database table.Table Splitting allows one DbContext to map multiple entity types to the same table, useful for representing different domain concepts that share underlying storage.Vehicle class designed with a private list of Owners and a Vin (Vehicle Identification Number) property.Vin is implemented as a record (value object) that encapsulates VIN validation logic.Owner has an ID and Name (also a value object for First/LastName), and From/To dates for ownership periods.Vehicles, People, VehicleOwners, and Addresses tables with appropriate columns, primary keys, foreign keys, and indexes.OnModelCreating to avoid data annotations and adhere to clean domain objects.Shadow property for ID (HasKey("ID")) so the entity doesn't need a public ID property, as Vin is the natural identifier.HasConversion is used for Vin to tell EF how to convert the Vin value object to a string for the database and vice-versa.AutoInclude() is used for Owners to ensure they are always loaded with the Vehicle, assuming the collection isn't excessively large.Owner to the VehicleOwners table, using Person as a private field mapped to the People table through HasOne().WithOne().HasForeignKey().Composite key (VehicleId, PersonId) is defined.HasComplexProperty is used for the Name value object, mapping its FirstName and LastName properties to corresponding database columns.IEntityTypeConfiguration<T> to separate mapping logic into dedicated classes (PersonTypeConfiguration, AddressTypeConfiguration) for better organization.Discriminator to map DeliveryAddress and InvoiceAddress (inherited from an abstract Address base class) to the same Addresses table based on a Type shadow property. This allows EF to understand the inheritance hierarchy in the database.GetVehicleByVin uses a custom extension method (VehicleWithVin) on DbContext to query for the Vehicle entity using the Vin value object.GetVehicleById demonstrates querying by a shadow property (EF.Property<int>(x, "ID")) not explicitly present on the entity.GetCurrentOwnerWithVin illustrates using LINQ projections to select only specific data needed for a DTO, avoiding loading entire objects for read operations.AddVehicle and SetCurrentOwner methods are simplified as the business logic is now encapsulated within the Vehicle entity, making the service code clean and focused on orchestration.Person entity implements an IHasId<int> interface, allowing controlled access to its ID property without making it publicly mutable or visible by default. This maintains encapsulation while enabling ID-based operations when explicitly cast.ToModel) are created to convert domain entities into DTOs for returning data from services, avoiding external mappers like AutoMapper for basic cases.
IDesignTimeDbContextFactory is used to provide a context for EF Core tools (like migrations bundle) without needing a full application startup.
AutoInclude() instead of lazy-loading [01:09:00]AutoInclude() can load all necessary data without performance issues, reducing the need for potentially problematic lazy loading.projections to select only the necessary columns into a DTO or even write raw SQL queries mapped to non-mapped entities if EF's LINQ translation is insufficient (EF Core 8+ supports this).Include() statements generates a Cartesian product and pulls too much redundant data, use AsSplitQuery() to tell EF to send multiple, simpler queries to the database.
Program.cs) is problematic in load-balanced environments, as multiple instances trying to migrate simultaneously can lead to failures and application downtime.EF migrations bundle executable (dotnet ef migrations bundle -o migration.exe) and run this tool as a separate step in your deployment pipeline.