EF 5-6.1.1中的NullReferenceException,具有相同类型的两个导航属性

我想首先谈谈我有一个解决这个问题的方法 – 但我今天花了几个小时搞清楚exception的原因,所以我想我会分享

给定域中的两个实体:

public class User { public int Id { get; set; } public string Name { get; set; } } public class Ticket { public int Id { get; set; } public string Name { get; set; } public virtual User Owner { get; set; } public int? LockedByUserId { get; set; } public virtual User LockedByUser { get; set; } 1541648976 public byte[] ETag { get; set; } } 

以下配置:

 public class TicketConfiguration : EntityTypeConfiguration { public TicketConfiguration() { HasRequired(x => x.Owner); HasOptional(x => x.LockedByUser) .WithMany() .HasForeignKey(x => x.LockedByUserId); Property(x => x.ETag) .IsConcurrencyToken(true); } } 

而这粒种子:

 protected override void Seed(DataContext context) { var users = context.Set(); var user = new User { Name = "Foo" }; users.AddOrUpdate(x => x.Name, user); user = users.SingleOrDefault(x => x.Name == "Foo") ?? user; var tickets = context.Set(); tickets.AddOrUpdate(x=>x.Name, new Ticket { Name = "Bar", Owner = user, }); } 

我得到一个例外:

 static void Main() { var config = new Migrations.Configuration { CommandTimeout = 3600 }; var migrator = new DbMigrator(config); migrator.Update(); using (var transaction = GetTransaction()) // I've tried with and without transaction { var context = new DataContext(); var userId = context.Set().Where(x=>x.Name == "Foo").Select(x=>x.Id).Single(); var ticket = context.Set().Single(x=>x.Name == "Bar"); ticket.LockedByUserId = userId; context.SaveChanges(); // Exception thrown here 'System.NullReferenceException' //at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.GetOtherEndOfRelationship(IEntityWrapper wrappedEntity) //at System.Data.Entity.Core.Objects.EntityEntry.AddRelationshipDetectedByForeignKey(Dictionary`2 relationships, Dictionary`2 principalRelationships, EntityKey relatedKey, EntityEntry relatedEntry, RelatedEnd relatedEndFrom) //at System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInForeignKeys() //at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChangesInForeignKeys(IList`1 entries) //at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges() //at System.Data.Entity.Core.Objects.ObjectContext.DetectChanges() //at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force) //at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate) //at System.Data.Entity.Internal.InternalContext.GetStateEntries() //at System.Data.Entity.Infrastructure.DbChangeTracker.Entries() //at System.Data.Entity.DbContext.GetValidationErrors() //at System.Data.Entity.Internal.InternalContext.SaveChanges() //at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() //at System.Data.Entity.DbContext.SaveChanges() //at EntityFrameworkFkNull.Program.Main(String[] args) in h:\Projects\Spikes\EntityFrameworkFkNull\EntityFrameworkFkNull\Program.cs:line 27 //at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) //at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) //at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() //at System.Threading.ThreadHelper.ThreadStart_Context(Object state) //at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) //at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) //at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) //at System.Threading.ThreadHelper.ThreadStart() transaction.Complete(); } } 

获取完整的解决方案,在这里尝试自己: https : //github.com/mvidacovich/EntityFrameworkFkNull

我相信这是因为Ticket有两个不同的外键用户,但只有其中一个是显式配置的。

就我自己测试而言,这会影响EF 5到Ef 6。

所以,这引出了一个问题: EF是否会在那里抛出exception?

解决方法是在Ticket上具有“OwnerId”属性。 (请参阅github中解决方案中的修复分支)

所以,Ticket成为:

 public class Ticket { public int Id { get; set; } public string Name { get; set; } public int? OwnerId { get; set; } public virtual User Owner { get; set; } public int? LockedByUserId { get; set; } public virtual User LockedByUser { get; set; } 1541648976 public byte[] ETag { get; set; } } 

并且Ticket的配置更改为:

 public class TicketConfiguration : EntityTypeConfiguration { public TicketConfiguration() { HasRequired(x => x.Owner) .WithMany() .HasForeignKey(x=>x.OwnerId); Property(x => x.OwnerId) .HasColumnName("Owner_Id"); HasOptional(x => x.LockedByUser) .WithMany() .HasForeignKey(x => x.LockedByUserId); Property(x => x.ETag) .IsConcurrencyToken(true); } } 

现在注意显式的OwnerId。 查看完整(固定)解决方案: https : //github.com/mvidacovich/EntityFrameworkFkNull/tree/Fix