Skip to content

Instantly share code, notes, and snippets.

@breyed
Created January 5, 2019 18:21
Show Gist options
  • Save breyed/f4487cee1e4a6921659829decc29f5c0 to your computer and use it in GitHub Desktop.
Save breyed/f4487cee1e4a6921659829decc29f5c0 to your computer and use it in GitHub Desktop.
Repro: Nested entity silently ignored if relationship not configured
#define FIRSTRUN
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace EfBug
{
class BaseContext : DbContext
{
readonly bool defineRelationships;
public BaseContext(bool defineRelationships) : base(new DbContextOptionsBuilder()
.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=ReproNestedEntityIgnored;Trusted_Connection=True;MultipleActiveResultSets=true").ConfigureWarnings(o => o.Throw(RelationalEventId.QueryClientEvaluationWarning)).Options)
{
this.defineRelationships = defineRelationships;
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var branch = modelBuilder.Entity<Branch>();
branch.HasOne(e => e.Location).WithMany().OnDelete(DeleteBehavior.Restrict);
if (defineRelationships) {
var transaction = modelBuilder.Entity<Transaction>();
transaction.HasOne(e => e.Branch).WithMany().OnDelete(DeleteBehavior.Restrict);
var location = modelBuilder.Entity<Location>();
location.HasOne(e => e.ServiceLocationCustomer).WithMany().IsRequired(false).OnDelete(DeleteBehavior.Restrict);
var invoice = modelBuilder.Entity<Invoice>();
invoice.HasOne(e => e.Customer).WithMany().OnDelete(DeleteBehavior.Restrict);
var customer = modelBuilder.Entity<Customer>();
customer.HasOne(e => e.Branch).WithMany().OnDelete(DeleteBehavior.Restrict);
}
}
public DbSet<Transaction> Transactions { get; private set; }
public DbSet<Invoice> Invoices { get; private set; }
public DbSet<Location> Locations { get; private set; }
public DbSet<Customer> Customers { get; private set; }
}
class FullContext : BaseContext
{
public FullContext() : base(true) { }
}
class PartialContext : BaseContext
{
public PartialContext() : base(false) { }
}
internal class Program
{
private static void Main(string[] args)
{
#if FIRSTRUN
Create();
Query("Base(true)", new BaseContext(true));
#endif
#if SECONDRUN
Query("Base(false)", new BaseContext(false));
#endif
Query("Full", new FullContext());
Query("Partial", new PartialContext());
Console.ReadKey();
}
static void Create()
{
using (var db = new BaseContext(true)) {
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var branch = db.Add(new Branch { BranchNumber = "B" }).Entity;
db.SaveChanges();
branch.Location = new Location { BranchId = branch.Id, ExternalId = 1 };
var customer = db.Add(new Customer { BranchId = branch.Id, AccountNumber = "A", BillingLocation = new Location { BranchId = branch.Id, ExternalId = 2 } }).Entity;
db.SaveChanges();
var invoice = db.Add(new Invoice {
InvoiceNumber = "I",
CustomerId = customer.Id,
ServiceLocation = new Location { BranchId = branch.Id, ExternalId = 3, ServiceLocationCustomerId = customer.Id },
}).Entity;
db.SaveChanges();
db.Add(new Transaction { BranchId = branch.Id, ExternalId = "E", ServiceLocationId = invoice.ServiceLocationId, InvoiceId = invoice.Id });
db.SaveChanges();
}
}
static void Query(string name, BaseContext db)
{
var result = (
from e in db.Transactions
where e.Id == 1
select new {
Entity = e,
e.ServiceLocation.ServiceLocationCustomer.AccountNumber,
e.ServiceLocation.ExternalId,
e.Invoice.InvoiceNumber,
}).Single();
Console.WriteLine($"{name} context: Account number = {result.AccountNumber}");
}
}
public class Branch
{
public int Id { get; set; }
[Required]
public string BranchNumber { get; set; }
public Location Location { get; set; }
public int? LocationId { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public Branch Branch { get; set; } // Non-normative foreign key required for uniqueness constraint on BranchId,ExternalId. (Uniqueness constraint not included in repro.)
public int BranchId { get; set; }
[Required]
public string ExternalId { get; set; }
public Location ServiceLocation { get; set; }
public int ServiceLocationId { get; set; }
public Invoice Invoice { get; set; }
public int? InvoiceId { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public Customer Customer { get; set; } // Non-normative foreign key required for uniqueness constraint on CustomerId,InvoiceNumber. (Uniqueness constraint not included in repro.)
public int CustomerId { get; set; }
[Required]
public string InvoiceNumber { get; set; }
public Location ServiceLocation { get; set; }
public int ServiceLocationId { get; set; }
}
public class Location
{
public int Id { get; set; }
public Branch Branch { get; set; }
public int BranchId { get; set; }
public Customer ServiceLocationCustomer { get; set; }
public int? ServiceLocationCustomerId { get; set; }
public int? ExternalId { get; set; }
}
public class Customer
{
public int Id { get; set; }
public Branch Branch { get; set; } // Non-normative foreign key required for uniqueness constraint on BranchId,AccountNumber. (Uniqueness constraint not included in repro.)
public int BranchId { get; set; }
[Required]
public string AccountNumber { get; set; }
[ForeignKey("BillingLocationId")]
public Location BillingLocation { get; set; }
public int BillingLocationId { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment