Entity Framework Core: self referencing FK column value being replaced with `NULL`











up vote
2
down vote

favorite












Target Framework: .Net Core 2.1



EntityFrameworkCore: 2.1.4



I've encountered some behaviour I don't understand when calling SaveChanges(). As the title says: I'm having valid GUIDs that have a Foreign Key relation with the same table being replaced with NULLs when inserting new records.



This is only happening for a specific column though, and only when EF generates the SQL for the changes made in the context. I can insert the same values fine inside SSMS.



For the purposes of demonstration I have created a minimal repository on GitHub that you can use to recreate the exact problem I'm encountering, but in a nutshell if you have an entity like this:



public class Account
{
[Key]
public Guid AccountId { get; set; }

#region Audit

public Guid? AddedByAccountId { get; set; }

public DateTime AddedOnUtc { get; set; }

public Guid? ModifiedByAccountId { get; set; }

public DateTime ModifiedOnUtc { get; set; }

#endregion Audit

#region Navigation Properties

[ForeignKey(nameof(AddedByAccountId))]
public virtual Account AddedByAccount { get; set; }

[ForeignKey(nameof(ModifiedByAccountId))]
public virtual Account ModifiedByAccount { get; set; }

#endregion Navigation Properties
}


And a derived DbContext implementation like this:



public class EntityFrameworkDbContext : DbContext
{
public EntityFrameworkDbContext(DbContextOptions<EntityFrameworkDbContext> options)
: base(options)
{
}

public DbSet<Account> Accounts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>(entity =>
{
// For some reason this self referencing key generates a unique
// constraint in the migration script if we don't set this here.

// "ModifiedByAccountId" does not have this issue! I suspect this might
// the root cause of the NULL data issue.
entity.HasIndex(e => e.AddedByAccountId).IsUnique(false);
});
}
}


Inserting anything into the context should yield the same results. For example:



private void InitialiseDatabase()
{
var systemAccount = new Account
{
AccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
AddedByAccountId = null,
AddedOnUtc = DateTime.UtcNow,
ModifiedByAccountId = null,
ModifiedOnUtc = DateTime.UtcNow
};

var otherAccounts = new List<Account>
{
new Account
{
AccountId = Guid.Parse("015b76fc-2833-45d9-85a7-ab1c389c1c11"),
AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
AddedOnUtc = DateTime.UtcNow,
ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
ModifiedOnUtc = DateTime.UtcNow
},
new Account
{
AccountId = Guid.Parse("538ee0dd-531a-41c6-8414-0769ec5990d8"),
AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
AddedOnUtc = DateTime.UtcNow,
ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
ModifiedOnUtc = DateTime.UtcNow
},
new Account
{
AccountId = Guid.Parse("8288d9ac-fbce-417e-89ef-82266b284b78"),
AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
AddedOnUtc = DateTime.UtcNow,
ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
ModifiedOnUtc = DateTime.UtcNow
},
new Account
{
AccountId = Guid.Parse("4bcfe9f8-e4a5-49f0-b6ee-44871632a903"),
AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
AddedOnUtc = DateTime.UtcNow,
ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
ModifiedOnUtc = DateTime.UtcNow
}
};

_dbContext.Add(systemAccount);
_dbContext.AddRange(otherAccounts);

try
{
_dbContext.SaveChanges();
}
catch (Exception ex)
{
// Just checking to see if anything was being raised.
}
}


Will generate the following SQL (captured using SQL Server Profiler and formatted for readability):



exec sp_executesql N'SET NOCOUNT ON;

INSERT INTO [Accounts]
(
[AccountId],
[AddedByAccountId],
[AddedOnUtc],
[ModifiedByAccountId],
[ModifiedOnUtc]
)
VALUES
(
@p5,
@p6,
@p7,
@p8,
@p9
),
(
@p10,
@p11,
@p12,
@p13,
@p14
),
(
@p15,
@p16,
@p17,
@p18,
@p19
),
(
@p20,
@p21,
@p22,
@p23,
@p24
);
',N'@p5 uniqueidentifier,
@p6 uniqueidentifier,
@p7 datetime2(7),
@p8 uniqueidentifier,
@p9 datetime2(7),
@p10 uniqueidentifier,
@p11 uniqueidentifier,
@p12 datetime2(7),
@p13 uniqueidentifier,
@p14 datetime2(7),
@p15 uniqueidentifier,
@p16 uniqueidentifier,
@p17 datetime2(7),
@p18 uniqueidentifier,
@p19 datetime2(7),
@p20 uniqueidentifier,
@p21 uniqueidentifier,
@p22 datetime2(7),
@p23 uniqueidentifier,
@p24 datetime2(7)',
@p5='015B76FC-2833-45D9-85A7-AB1C389C1C11',
@p6=NULL,
@p7='2018-11-10 14:29:25.5363017',
@p8='35C38DF0-A959-4232-AADD-40DB2260F557',
@p9='2018-11-10 14:29:25.5363022',
@p10='538EE0DD-531A-41C6-8414-0769EC5990D8',
@p11=NULL,
@p12='2018-11-10 14:29:25.5363031',
@p13='35C38DF0-A959-4232-AADD-40DB2260F557',
@p14='2018-11-10 14:29:25.5363034',
@p15='8288D9AC-FBCE-417E-89EF-82266B284B78',
@p16=NULL,
@p17='2018-11-10 14:29:25.5363039',
@p18='35C38DF0-A959-4232-AADD-40DB2260F557',
@p19='2018-11-10 14:29:25.5363042',
@p20='4BCFE9F8-E4A5-49F0-B6EE-44871632A903',
@p21='35C38DF0-A959-4232-AADD-40DB2260F557',
@p22='2018-11-10 14:29:25.5363047',
@p23='35C38DF0-A959-4232-AADD-40DB2260F557',
@p24='2018-11-10 14:29:25.5363047'


I suspect that this issue has its roots in the "IsUnique(false") line of code I highlighted in the second snippet as this only happens for the AddedByAccountId property/column. However, if I don't include this line then the migration script created will add on a unique constraint (which I don't want).



Has anyone encountered issues with either constraints being created when they shouldn't or (more importantly) NULL values replacing actual data on insert?



Cheers.










share|improve this question




























    up vote
    2
    down vote

    favorite












    Target Framework: .Net Core 2.1



    EntityFrameworkCore: 2.1.4



    I've encountered some behaviour I don't understand when calling SaveChanges(). As the title says: I'm having valid GUIDs that have a Foreign Key relation with the same table being replaced with NULLs when inserting new records.



    This is only happening for a specific column though, and only when EF generates the SQL for the changes made in the context. I can insert the same values fine inside SSMS.



    For the purposes of demonstration I have created a minimal repository on GitHub that you can use to recreate the exact problem I'm encountering, but in a nutshell if you have an entity like this:



    public class Account
    {
    [Key]
    public Guid AccountId { get; set; }

    #region Audit

    public Guid? AddedByAccountId { get; set; }

    public DateTime AddedOnUtc { get; set; }

    public Guid? ModifiedByAccountId { get; set; }

    public DateTime ModifiedOnUtc { get; set; }

    #endregion Audit

    #region Navigation Properties

    [ForeignKey(nameof(AddedByAccountId))]
    public virtual Account AddedByAccount { get; set; }

    [ForeignKey(nameof(ModifiedByAccountId))]
    public virtual Account ModifiedByAccount { get; set; }

    #endregion Navigation Properties
    }


    And a derived DbContext implementation like this:



    public class EntityFrameworkDbContext : DbContext
    {
    public EntityFrameworkDbContext(DbContextOptions<EntityFrameworkDbContext> options)
    : base(options)
    {
    }

    public DbSet<Account> Accounts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    modelBuilder.Entity<Account>(entity =>
    {
    // For some reason this self referencing key generates a unique
    // constraint in the migration script if we don't set this here.

    // "ModifiedByAccountId" does not have this issue! I suspect this might
    // the root cause of the NULL data issue.
    entity.HasIndex(e => e.AddedByAccountId).IsUnique(false);
    });
    }
    }


    Inserting anything into the context should yield the same results. For example:



    private void InitialiseDatabase()
    {
    var systemAccount = new Account
    {
    AccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    AddedByAccountId = null,
    AddedOnUtc = DateTime.UtcNow,
    ModifiedByAccountId = null,
    ModifiedOnUtc = DateTime.UtcNow
    };

    var otherAccounts = new List<Account>
    {
    new Account
    {
    AccountId = Guid.Parse("015b76fc-2833-45d9-85a7-ab1c389c1c11"),
    AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    AddedOnUtc = DateTime.UtcNow,
    ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    ModifiedOnUtc = DateTime.UtcNow
    },
    new Account
    {
    AccountId = Guid.Parse("538ee0dd-531a-41c6-8414-0769ec5990d8"),
    AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    AddedOnUtc = DateTime.UtcNow,
    ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    ModifiedOnUtc = DateTime.UtcNow
    },
    new Account
    {
    AccountId = Guid.Parse("8288d9ac-fbce-417e-89ef-82266b284b78"),
    AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    AddedOnUtc = DateTime.UtcNow,
    ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    ModifiedOnUtc = DateTime.UtcNow
    },
    new Account
    {
    AccountId = Guid.Parse("4bcfe9f8-e4a5-49f0-b6ee-44871632a903"),
    AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    AddedOnUtc = DateTime.UtcNow,
    ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
    ModifiedOnUtc = DateTime.UtcNow
    }
    };

    _dbContext.Add(systemAccount);
    _dbContext.AddRange(otherAccounts);

    try
    {
    _dbContext.SaveChanges();
    }
    catch (Exception ex)
    {
    // Just checking to see if anything was being raised.
    }
    }


    Will generate the following SQL (captured using SQL Server Profiler and formatted for readability):



    exec sp_executesql N'SET NOCOUNT ON;

    INSERT INTO [Accounts]
    (
    [AccountId],
    [AddedByAccountId],
    [AddedOnUtc],
    [ModifiedByAccountId],
    [ModifiedOnUtc]
    )
    VALUES
    (
    @p5,
    @p6,
    @p7,
    @p8,
    @p9
    ),
    (
    @p10,
    @p11,
    @p12,
    @p13,
    @p14
    ),
    (
    @p15,
    @p16,
    @p17,
    @p18,
    @p19
    ),
    (
    @p20,
    @p21,
    @p22,
    @p23,
    @p24
    );
    ',N'@p5 uniqueidentifier,
    @p6 uniqueidentifier,
    @p7 datetime2(7),
    @p8 uniqueidentifier,
    @p9 datetime2(7),
    @p10 uniqueidentifier,
    @p11 uniqueidentifier,
    @p12 datetime2(7),
    @p13 uniqueidentifier,
    @p14 datetime2(7),
    @p15 uniqueidentifier,
    @p16 uniqueidentifier,
    @p17 datetime2(7),
    @p18 uniqueidentifier,
    @p19 datetime2(7),
    @p20 uniqueidentifier,
    @p21 uniqueidentifier,
    @p22 datetime2(7),
    @p23 uniqueidentifier,
    @p24 datetime2(7)',
    @p5='015B76FC-2833-45D9-85A7-AB1C389C1C11',
    @p6=NULL,
    @p7='2018-11-10 14:29:25.5363017',
    @p8='35C38DF0-A959-4232-AADD-40DB2260F557',
    @p9='2018-11-10 14:29:25.5363022',
    @p10='538EE0DD-531A-41C6-8414-0769EC5990D8',
    @p11=NULL,
    @p12='2018-11-10 14:29:25.5363031',
    @p13='35C38DF0-A959-4232-AADD-40DB2260F557',
    @p14='2018-11-10 14:29:25.5363034',
    @p15='8288D9AC-FBCE-417E-89EF-82266B284B78',
    @p16=NULL,
    @p17='2018-11-10 14:29:25.5363039',
    @p18='35C38DF0-A959-4232-AADD-40DB2260F557',
    @p19='2018-11-10 14:29:25.5363042',
    @p20='4BCFE9F8-E4A5-49F0-B6EE-44871632A903',
    @p21='35C38DF0-A959-4232-AADD-40DB2260F557',
    @p22='2018-11-10 14:29:25.5363047',
    @p23='35C38DF0-A959-4232-AADD-40DB2260F557',
    @p24='2018-11-10 14:29:25.5363047'


    I suspect that this issue has its roots in the "IsUnique(false") line of code I highlighted in the second snippet as this only happens for the AddedByAccountId property/column. However, if I don't include this line then the migration script created will add on a unique constraint (which I don't want).



    Has anyone encountered issues with either constraints being created when they shouldn't or (more importantly) NULL values replacing actual data on insert?



    Cheers.










    share|improve this question


























      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      Target Framework: .Net Core 2.1



      EntityFrameworkCore: 2.1.4



      I've encountered some behaviour I don't understand when calling SaveChanges(). As the title says: I'm having valid GUIDs that have a Foreign Key relation with the same table being replaced with NULLs when inserting new records.



      This is only happening for a specific column though, and only when EF generates the SQL for the changes made in the context. I can insert the same values fine inside SSMS.



      For the purposes of demonstration I have created a minimal repository on GitHub that you can use to recreate the exact problem I'm encountering, but in a nutshell if you have an entity like this:



      public class Account
      {
      [Key]
      public Guid AccountId { get; set; }

      #region Audit

      public Guid? AddedByAccountId { get; set; }

      public DateTime AddedOnUtc { get; set; }

      public Guid? ModifiedByAccountId { get; set; }

      public DateTime ModifiedOnUtc { get; set; }

      #endregion Audit

      #region Navigation Properties

      [ForeignKey(nameof(AddedByAccountId))]
      public virtual Account AddedByAccount { get; set; }

      [ForeignKey(nameof(ModifiedByAccountId))]
      public virtual Account ModifiedByAccount { get; set; }

      #endregion Navigation Properties
      }


      And a derived DbContext implementation like this:



      public class EntityFrameworkDbContext : DbContext
      {
      public EntityFrameworkDbContext(DbContextOptions<EntityFrameworkDbContext> options)
      : base(options)
      {
      }

      public DbSet<Account> Accounts { get; set; }

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
      modelBuilder.Entity<Account>(entity =>
      {
      // For some reason this self referencing key generates a unique
      // constraint in the migration script if we don't set this here.

      // "ModifiedByAccountId" does not have this issue! I suspect this might
      // the root cause of the NULL data issue.
      entity.HasIndex(e => e.AddedByAccountId).IsUnique(false);
      });
      }
      }


      Inserting anything into the context should yield the same results. For example:



      private void InitialiseDatabase()
      {
      var systemAccount = new Account
      {
      AccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedByAccountId = null,
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = null,
      ModifiedOnUtc = DateTime.UtcNow
      };

      var otherAccounts = new List<Account>
      {
      new Account
      {
      AccountId = Guid.Parse("015b76fc-2833-45d9-85a7-ab1c389c1c11"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("538ee0dd-531a-41c6-8414-0769ec5990d8"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("8288d9ac-fbce-417e-89ef-82266b284b78"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("4bcfe9f8-e4a5-49f0-b6ee-44871632a903"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      }
      };

      _dbContext.Add(systemAccount);
      _dbContext.AddRange(otherAccounts);

      try
      {
      _dbContext.SaveChanges();
      }
      catch (Exception ex)
      {
      // Just checking to see if anything was being raised.
      }
      }


      Will generate the following SQL (captured using SQL Server Profiler and formatted for readability):



      exec sp_executesql N'SET NOCOUNT ON;

      INSERT INTO [Accounts]
      (
      [AccountId],
      [AddedByAccountId],
      [AddedOnUtc],
      [ModifiedByAccountId],
      [ModifiedOnUtc]
      )
      VALUES
      (
      @p5,
      @p6,
      @p7,
      @p8,
      @p9
      ),
      (
      @p10,
      @p11,
      @p12,
      @p13,
      @p14
      ),
      (
      @p15,
      @p16,
      @p17,
      @p18,
      @p19
      ),
      (
      @p20,
      @p21,
      @p22,
      @p23,
      @p24
      );
      ',N'@p5 uniqueidentifier,
      @p6 uniqueidentifier,
      @p7 datetime2(7),
      @p8 uniqueidentifier,
      @p9 datetime2(7),
      @p10 uniqueidentifier,
      @p11 uniqueidentifier,
      @p12 datetime2(7),
      @p13 uniqueidentifier,
      @p14 datetime2(7),
      @p15 uniqueidentifier,
      @p16 uniqueidentifier,
      @p17 datetime2(7),
      @p18 uniqueidentifier,
      @p19 datetime2(7),
      @p20 uniqueidentifier,
      @p21 uniqueidentifier,
      @p22 datetime2(7),
      @p23 uniqueidentifier,
      @p24 datetime2(7)',
      @p5='015B76FC-2833-45D9-85A7-AB1C389C1C11',
      @p6=NULL,
      @p7='2018-11-10 14:29:25.5363017',
      @p8='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p9='2018-11-10 14:29:25.5363022',
      @p10='538EE0DD-531A-41C6-8414-0769EC5990D8',
      @p11=NULL,
      @p12='2018-11-10 14:29:25.5363031',
      @p13='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p14='2018-11-10 14:29:25.5363034',
      @p15='8288D9AC-FBCE-417E-89EF-82266B284B78',
      @p16=NULL,
      @p17='2018-11-10 14:29:25.5363039',
      @p18='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p19='2018-11-10 14:29:25.5363042',
      @p20='4BCFE9F8-E4A5-49F0-B6EE-44871632A903',
      @p21='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p22='2018-11-10 14:29:25.5363047',
      @p23='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p24='2018-11-10 14:29:25.5363047'


      I suspect that this issue has its roots in the "IsUnique(false") line of code I highlighted in the second snippet as this only happens for the AddedByAccountId property/column. However, if I don't include this line then the migration script created will add on a unique constraint (which I don't want).



      Has anyone encountered issues with either constraints being created when they shouldn't or (more importantly) NULL values replacing actual data on insert?



      Cheers.










      share|improve this question















      Target Framework: .Net Core 2.1



      EntityFrameworkCore: 2.1.4



      I've encountered some behaviour I don't understand when calling SaveChanges(). As the title says: I'm having valid GUIDs that have a Foreign Key relation with the same table being replaced with NULLs when inserting new records.



      This is only happening for a specific column though, and only when EF generates the SQL for the changes made in the context. I can insert the same values fine inside SSMS.



      For the purposes of demonstration I have created a minimal repository on GitHub that you can use to recreate the exact problem I'm encountering, but in a nutshell if you have an entity like this:



      public class Account
      {
      [Key]
      public Guid AccountId { get; set; }

      #region Audit

      public Guid? AddedByAccountId { get; set; }

      public DateTime AddedOnUtc { get; set; }

      public Guid? ModifiedByAccountId { get; set; }

      public DateTime ModifiedOnUtc { get; set; }

      #endregion Audit

      #region Navigation Properties

      [ForeignKey(nameof(AddedByAccountId))]
      public virtual Account AddedByAccount { get; set; }

      [ForeignKey(nameof(ModifiedByAccountId))]
      public virtual Account ModifiedByAccount { get; set; }

      #endregion Navigation Properties
      }


      And a derived DbContext implementation like this:



      public class EntityFrameworkDbContext : DbContext
      {
      public EntityFrameworkDbContext(DbContextOptions<EntityFrameworkDbContext> options)
      : base(options)
      {
      }

      public DbSet<Account> Accounts { get; set; }

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
      modelBuilder.Entity<Account>(entity =>
      {
      // For some reason this self referencing key generates a unique
      // constraint in the migration script if we don't set this here.

      // "ModifiedByAccountId" does not have this issue! I suspect this might
      // the root cause of the NULL data issue.
      entity.HasIndex(e => e.AddedByAccountId).IsUnique(false);
      });
      }
      }


      Inserting anything into the context should yield the same results. For example:



      private void InitialiseDatabase()
      {
      var systemAccount = new Account
      {
      AccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedByAccountId = null,
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = null,
      ModifiedOnUtc = DateTime.UtcNow
      };

      var otherAccounts = new List<Account>
      {
      new Account
      {
      AccountId = Guid.Parse("015b76fc-2833-45d9-85a7-ab1c389c1c11"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("538ee0dd-531a-41c6-8414-0769ec5990d8"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("8288d9ac-fbce-417e-89ef-82266b284b78"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      },
      new Account
      {
      AccountId = Guid.Parse("4bcfe9f8-e4a5-49f0-b6ee-44871632a903"),
      AddedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      AddedOnUtc = DateTime.UtcNow,
      ModifiedByAccountId = Guid.Parse("35c38df0-a959-4232-aadd-40db2260f557"),
      ModifiedOnUtc = DateTime.UtcNow
      }
      };

      _dbContext.Add(systemAccount);
      _dbContext.AddRange(otherAccounts);

      try
      {
      _dbContext.SaveChanges();
      }
      catch (Exception ex)
      {
      // Just checking to see if anything was being raised.
      }
      }


      Will generate the following SQL (captured using SQL Server Profiler and formatted for readability):



      exec sp_executesql N'SET NOCOUNT ON;

      INSERT INTO [Accounts]
      (
      [AccountId],
      [AddedByAccountId],
      [AddedOnUtc],
      [ModifiedByAccountId],
      [ModifiedOnUtc]
      )
      VALUES
      (
      @p5,
      @p6,
      @p7,
      @p8,
      @p9
      ),
      (
      @p10,
      @p11,
      @p12,
      @p13,
      @p14
      ),
      (
      @p15,
      @p16,
      @p17,
      @p18,
      @p19
      ),
      (
      @p20,
      @p21,
      @p22,
      @p23,
      @p24
      );
      ',N'@p5 uniqueidentifier,
      @p6 uniqueidentifier,
      @p7 datetime2(7),
      @p8 uniqueidentifier,
      @p9 datetime2(7),
      @p10 uniqueidentifier,
      @p11 uniqueidentifier,
      @p12 datetime2(7),
      @p13 uniqueidentifier,
      @p14 datetime2(7),
      @p15 uniqueidentifier,
      @p16 uniqueidentifier,
      @p17 datetime2(7),
      @p18 uniqueidentifier,
      @p19 datetime2(7),
      @p20 uniqueidentifier,
      @p21 uniqueidentifier,
      @p22 datetime2(7),
      @p23 uniqueidentifier,
      @p24 datetime2(7)',
      @p5='015B76FC-2833-45D9-85A7-AB1C389C1C11',
      @p6=NULL,
      @p7='2018-11-10 14:29:25.5363017',
      @p8='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p9='2018-11-10 14:29:25.5363022',
      @p10='538EE0DD-531A-41C6-8414-0769EC5990D8',
      @p11=NULL,
      @p12='2018-11-10 14:29:25.5363031',
      @p13='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p14='2018-11-10 14:29:25.5363034',
      @p15='8288D9AC-FBCE-417E-89EF-82266B284B78',
      @p16=NULL,
      @p17='2018-11-10 14:29:25.5363039',
      @p18='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p19='2018-11-10 14:29:25.5363042',
      @p20='4BCFE9F8-E4A5-49F0-B6EE-44871632A903',
      @p21='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p22='2018-11-10 14:29:25.5363047',
      @p23='35C38DF0-A959-4232-AADD-40DB2260F557',
      @p24='2018-11-10 14:29:25.5363047'


      I suspect that this issue has its roots in the "IsUnique(false") line of code I highlighted in the second snippet as this only happens for the AddedByAccountId property/column. However, if I don't include this line then the migration script created will add on a unique constraint (which I don't want).



      Has anyone encountered issues with either constraints being created when they shouldn't or (more importantly) NULL values replacing actual data on insert?



      Cheers.







      c# sql-server entity-framework-core






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 10 at 15:33

























      asked Nov 10 at 15:22









      Michael Searson

      1314




      1314
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted











          // For some reason this self referencing key generates a unique

          // constraint in the migration script if we don't set this here.



          // "ModifiedByAccountId" does not have this issue! I suspect this might

          // the root cause of the NULL data issue.




          You suspect it right. EF Core is confused from the 2 self referencing navigation properties and wrongly decides (could be a bug) that they represent a single One-to-one relationship:




          One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.




          Of course what you want is two one-to-many relationships, so rather than fixing the index (which won't help as you already saw), just map them explicitly:



          modelBuilder.Entity<Account>().HasOne(e => e.AddedByAccount).WithMany();
          modelBuilder.Entity<Account>().HasOne(e => e.ModifiedByAccount).WithMany();





          share|improve this answer





















          • Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
            – Michael Searson
            Nov 10 at 18:48












          • But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
            – Ivan Stoev
            Nov 10 at 19:10










          • Ah - of course! Makes sense to me now. Cheers for your help with this!
            – Michael Searson
            Nov 10 at 19:25










          • Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
            – Michael Searson
            Nov 12 at 18:17











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53240371%2fentity-framework-core-self-referencing-fk-column-value-being-replaced-with-nul%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote



          accepted











          // For some reason this self referencing key generates a unique

          // constraint in the migration script if we don't set this here.



          // "ModifiedByAccountId" does not have this issue! I suspect this might

          // the root cause of the NULL data issue.




          You suspect it right. EF Core is confused from the 2 self referencing navigation properties and wrongly decides (could be a bug) that they represent a single One-to-one relationship:




          One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.




          Of course what you want is two one-to-many relationships, so rather than fixing the index (which won't help as you already saw), just map them explicitly:



          modelBuilder.Entity<Account>().HasOne(e => e.AddedByAccount).WithMany();
          modelBuilder.Entity<Account>().HasOne(e => e.ModifiedByAccount).WithMany();





          share|improve this answer





















          • Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
            – Michael Searson
            Nov 10 at 18:48












          • But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
            – Ivan Stoev
            Nov 10 at 19:10










          • Ah - of course! Makes sense to me now. Cheers for your help with this!
            – Michael Searson
            Nov 10 at 19:25










          • Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
            – Michael Searson
            Nov 12 at 18:17















          up vote
          1
          down vote



          accepted











          // For some reason this self referencing key generates a unique

          // constraint in the migration script if we don't set this here.



          // "ModifiedByAccountId" does not have this issue! I suspect this might

          // the root cause of the NULL data issue.




          You suspect it right. EF Core is confused from the 2 self referencing navigation properties and wrongly decides (could be a bug) that they represent a single One-to-one relationship:




          One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.




          Of course what you want is two one-to-many relationships, so rather than fixing the index (which won't help as you already saw), just map them explicitly:



          modelBuilder.Entity<Account>().HasOne(e => e.AddedByAccount).WithMany();
          modelBuilder.Entity<Account>().HasOne(e => e.ModifiedByAccount).WithMany();





          share|improve this answer





















          • Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
            – Michael Searson
            Nov 10 at 18:48












          • But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
            – Ivan Stoev
            Nov 10 at 19:10










          • Ah - of course! Makes sense to me now. Cheers for your help with this!
            – Michael Searson
            Nov 10 at 19:25










          • Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
            – Michael Searson
            Nov 12 at 18:17













          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted







          // For some reason this self referencing key generates a unique

          // constraint in the migration script if we don't set this here.



          // "ModifiedByAccountId" does not have this issue! I suspect this might

          // the root cause of the NULL data issue.




          You suspect it right. EF Core is confused from the 2 self referencing navigation properties and wrongly decides (could be a bug) that they represent a single One-to-one relationship:




          One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.




          Of course what you want is two one-to-many relationships, so rather than fixing the index (which won't help as you already saw), just map them explicitly:



          modelBuilder.Entity<Account>().HasOne(e => e.AddedByAccount).WithMany();
          modelBuilder.Entity<Account>().HasOne(e => e.ModifiedByAccount).WithMany();





          share|improve this answer













          // For some reason this self referencing key generates a unique

          // constraint in the migration script if we don't set this here.



          // "ModifiedByAccountId" does not have this issue! I suspect this might

          // the root cause of the NULL data issue.




          You suspect it right. EF Core is confused from the 2 self referencing navigation properties and wrongly decides (could be a bug) that they represent a single One-to-one relationship:




          One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.




          Of course what you want is two one-to-many relationships, so rather than fixing the index (which won't help as you already saw), just map them explicitly:



          modelBuilder.Entity<Account>().HasOne(e => e.AddedByAccount).WithMany();
          modelBuilder.Entity<Account>().HasOne(e => e.ModifiedByAccount).WithMany();






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 10 at 17:21









          Ivan Stoev

          95.4k764117




          95.4k764117












          • Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
            – Michael Searson
            Nov 10 at 18:48












          • But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
            – Ivan Stoev
            Nov 10 at 19:10










          • Ah - of course! Makes sense to me now. Cheers for your help with this!
            – Michael Searson
            Nov 10 at 19:25










          • Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
            – Michael Searson
            Nov 12 at 18:17


















          • Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
            – Michael Searson
            Nov 10 at 18:48












          • But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
            – Ivan Stoev
            Nov 10 at 19:10










          • Ah - of course! Makes sense to me now. Cheers for your help with this!
            – Michael Searson
            Nov 10 at 19:25










          • Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
            – Michael Searson
            Nov 12 at 18:17
















          Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
          – Michael Searson
          Nov 10 at 18:48






          Thank you for confirming my suspicions about the AddedByAccountId property. In regards to the solution it does indeed work - but the use of WithMany() does seem a bit misleading as the method summary says: "Configures this as a one-to-many relationship." Something that this relationship is not as it's one-to-one. If this is just a limitation of EF Core though I'm happy to mark this as the answer and raise this elsewhere.
          – Michael Searson
          Nov 10 at 18:48














          But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
          – Ivan Stoev
          Nov 10 at 19:10




          But it is one-to-many (actually two one-to-many as I mentioned in the answer). See - one account can add many other accounts. Same for modified. If you think in the opposite way that one account can be added by 0 or 1 accounts, you will be right, but in relational world the cardinality of the relationship is expressed from principal to dependent.
          – Ivan Stoev
          Nov 10 at 19:10












          Ah - of course! Makes sense to me now. Cheers for your help with this!
          – Michael Searson
          Nov 10 at 19:25




          Ah - of course! Makes sense to me now. Cheers for your help with this!
          – Michael Searson
          Nov 10 at 19:25












          Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
          – Michael Searson
          Nov 12 at 18:17




          Just updating this for future reference. I asked for clarification on the EF Core Github page and the solution provided by @Ivan is the correct one for allowing my intended "one-to-many" behaviour. If the unique constraints were required then the WithOne() extension method would be used for each property instead.
          – Michael Searson
          Nov 12 at 18:17


















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53240371%2fentity-framework-core-self-referencing-fk-column-value-being-replaced-with-nul%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Full-time equivalent

          Bicuculline

          さくらももこ