efcore
efcore copied to clipboard
FK for generated key using TPC sometimes gets temporary value persisted to database on SaveChanges
using (var context = new AnimalsContext())
{
await context.Database.EnsureDeletedAsync();
Console.WriteLine(context.Model.ToDebugString());
Console.WriteLine();
await context.Database.EnsureCreatedAsync();
var catFood = new PetFood("Lily's Kitchen", LifeStage.Adult);
var dogFood = new PetFood("Canagan", LifeStage.Adult);
var hay = new FarmFood("Hay");
var sushi = new HumanFood("Sushi", 670);
var arthur = new Human("Arthur") { Food = sushi };
var wendy = new Human("Wendy");
var christi = new Human("Christi");
var alice = new Cat("Alice", "MBA") { Vet = "Pengelly", Food = catFood, Humans = { arthur, wendy } };
var mac = new Cat("Mac", "Preschool") { Vet = "Pengelly", Food = catFood, Humans = { arthur, wendy } };
var toast = new Dog("Toast", "Mr. Squirrel") { Vet = "Pengelly", Food = dogFood, Humans = { arthur, wendy } };
var clyde = new FarmAnimal("Clyde", "Equus africanus asinus") { Value = 100.0m, Food = hay };
wendy.FavoriteAnimal = toast;
arthur.FavoriteAnimal = alice;
christi.FavoriteAnimal = clyde;
await context.AddRangeAsync(wendy, arthur, christi, alice, mac, toast, clyde);
await context.SaveChangesAsync();
}
Console.WriteLine();
using (var context = new AnimalsContext())
{
foreach (var human in context.Humans)
{
Console.WriteLine($"Human.FavoriteAnimalId = {context.Entry(human).Property("FavoriteAnimalId").CurrentValue}");
}
}
public abstract class Animal
{
protected Animal(string name)
{
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public abstract string Species { get; }
public Food? Food { get; set; }
}
public abstract class Pet : Animal
{
protected Pet(string name)
: base(name)
{
}
public string? Vet { get; set; }
public ICollection<Human> Humans { get; } = new List<Human>();
}
public class FarmAnimal : Animal
{
public FarmAnimal(string name, string species)
: base(name)
{
Species = species;
}
public override string Species { get; }
[Precision(18, 2)]
public decimal Value { get; set; }
public override string ToString()
=> $"Farm animal '{Name}' ({Species}/{Id}) worth {Value:C} eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Cat : Pet
{
public Cat(string name, string educationLevel)
: base(name)
{
EducationLevel = educationLevel;
}
public string EducationLevel { get; set; }
public override string Species
=> "Felis catus";
public override string ToString()
=> $"Cat '{Name}' ({Species}/{Id}) with education '{EducationLevel}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Dog : Pet
{
public Dog(string name, string favoriteToy)
: base(name)
{
FavoriteToy = favoriteToy;
}
public string FavoriteToy { get; set; }
public override string Species
=> "Canis familiaris";
public override string ToString()
=> $"Dog '{Name}' ({Species}/{Id}) with favorite toy '{FavoriteToy}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Human : Animal
{
public Human(string name)
: base(name)
{
}
public override string Species
=> "Homo sapiens";
public Animal? FavoriteAnimal { get; set; }
public ICollection<Pet> Pets { get; } = new List<Pet>();
public override string ToString()
=> $"Human '{Name}' ({Species}/{Id}) with favorite animal '{FavoriteAnimal?.Name ?? "<Unknown>"}'"
+ $" eats {Food?.ToString() ?? "<Unknown>"}";
}
public abstract class Food
{
public Guid Id { get; set; }
}
public class PetFood : Food
{
public PetFood(string brand, LifeStage lifeStage)
{
Brand = brand;
LifeStage = lifeStage;
}
public string Brand { get; set; }
public LifeStage LifeStage { get; set; }
public override string ToString()
=> $"Pet food by '{Brand}' ({Id}) for life stage {LifeStage}";
}
public enum LifeStage
{
Juvenile,
Adult,
Senior
}
public class HumanFood : Food
{
public HumanFood(string name, int calories)
{
Name = name;
Calories = calories;
}
[Column("Name")]
public string Name { get; set; }
public int Calories { get; set; }
public override string ToString()
=> $"{Name} ({Id}) with calories {Calories}";
}
public class FarmFood : Food
{
public FarmFood(string name)
{
Name = name;
}
[Column("Name")]
public string Name { get; set; }
public override string ToString()
=> $"{Name} ({Id})";
}
public class AnimalsContext : DbContext
{
public DbSet<Animal> Animals
=> Set<Animal>();
public DbSet<Pet> Pets
=> Set<Pet>();
public DbSet<FarmAnimal> FarmAnimals
=> Set<FarmAnimal>();
public DbSet<Cat> Cats
=> Set<Cat>();
public DbSet<Dog> Dogs
=> Set<Dog>();
public DbSet<Human> Humans
=> Set<Human>();
public DbSet<Food> Foods
=> Set<Food>();
public DbSet<PetFood> PetFoods
=> Set<PetFood>();
public DbSet<FarmFood> FarmFoods
=> Set<FarmFood>();
public DbSet<HumanFood> HumanFoods
=> Set<HumanFood>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.EnableSensitiveDataLogging()
.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}");
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
modelBuilder.Entity<Food>().UseTpcMappingStrategy();
modelBuilder.Entity<FarmAnimal>().Property(e => e.Species);
modelBuilder.Entity<Human>()
.HasMany(e => e.Pets)
.WithMany(e => e.Humans)
.UsingEntity<Dictionary<object, string>>(
"PetsHumans",
r => r.HasOne<Pet>().WithMany().OnDelete(DeleteBehavior.Cascade),
l => l.HasOne<Human>().WithMany().OnDelete(DeleteBehavior.ClientCascade));
}
}
Human.FavoriteAnimalId = -2147482646
Human.FavoriteAnimalId = -2147482644
Human.FavoriteAnimalId = -2147482642