Created
January 5, 2019 18:21
-
-
Save breyed/f4487cee1e4a6921659829decc29f5c0 to your computer and use it in GitHub Desktop.
Repro: Nested entity silently ignored if relationship not configured
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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