Skip to content

The property 'Id' on entity type 'EntityType' has a temporary value while attempting to change the entity's state #20200

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
TehWardy opened this issue Mar 6, 2020 · 27 comments

Comments

@TehWardy
Copy link

TehWardy commented Mar 6, 2020

image

Whilst making an insertion in to the db of an entity I've recently started receiving this and i can't see why ...

The property 'Id' on entity type 'Page' has a temporary value while attempting to change the entity's state to 'Unchanged'. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property.

The entity definition begins with ...

    [Table("Pages", Schema = "CMS")]
    public class Page
    {
        [Key]
        public int Id { get; set; }
        ...

... the resulting EF Core migration generates this ...

migrationBuilder.CreateTable(
    name: "Pages",
    schema: "CMS",
    columns: table => new
    {
        Id = table.Column<int>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),

Which in turn looks like this when run on the DB server ...

image

... I can't see any reason why the primary key for that table isn't entirely managed by EF / SQL here so when I do osmething like ...

var newPage = context.Add(new Page { ... });

... and leave the Id on it's default I would expect the newPage variable to be populated with a DB generated primary key value.

What am I missing here?

@TehWardy TehWardy changed the title Exception for temporary value on insert The property 'Id' on entity type 'EntityType' has a temporary value while attempting to change the entity's state Mar 6, 2020
@TehWardy
Copy link
Author

TehWardy commented Mar 6, 2020

Just found #9552 i'm not sure if it's related, but it appears to be my issue / at least the same symptom.

@ajcvickers
Copy link
Contributor

@TehWardy EF Core 2.2 is out-of-support. Please update to 3.1 and report back if you are still hitting this error.

@TehWardy
Copy link
Author

TehWardy commented Mar 6, 2020

Doing that is not possible at this time since it would introduce a ton of breaking changes and force a .Net Core framework version upgrade on me which I cannot implement due to missing / broken features in the OData framework.

@ajcvickers
Copy link
Contributor

@TehWardy The other option is to revert to EF Core 2.1 which is LTS. However, while it is not inconceivable that we would fix this as an LTS patch, I would say that it is very unlikely. I would suggest you attempt to work around it in your code until you can update.

@TehWardy
Copy link
Author

TehWardy commented Mar 6, 2020

I don't suppose you know the status of the other teams work?
I use WebAPI, OData and EF for the core of my web stack, last time I tried an update was late last year and everything just fell apart due to incomplete feature chains.

I did notice though that EF Core 3.1 is sat on .Net standard 2.0 as opposed to v3.0 which sits on 2.1 which was why I tried the upgrade in the first place.

Does this mean that EF Core 3.1 is compatible with .Net Core 2.2 or am I misunderstanding all the versioning links here?
My premise is that .Net Core 2.2 implements netstandard 2.0 and .Net Core 3.x implements netstandard 2.1.

Am I on the right track here?

@ajcvickers
Copy link
Contributor

@TehWardy I'm not sure what the status is with OData. Other things should work. OData is problematic because it generates "interesting" queries and it's very difficult to suggest workarounds because the query generation is all hidden.

EF Core 3.1 will work with any .NET Standard 2.0 platform, including .NET Core 2.2. However, use of ASP.NET 2.x with EF Core 3.1 may not work depending on what ASP.NET is using from EF Core. See supported platforms.

@TehWardy
Copy link
Author

TehWardy commented Mar 7, 2020

That right there is the source of most of my frustrations with all .Net stack based solutions.
It's like there is no official guideline about what is compatible with what and if you find a scenario that sits in the gap between two frameworks the answer is usually cryptic or vague.

The net result is often that simply explaining the issue is non trivial and even if you agree that my issue is a genuine "problem in the technology" I still have the issue of trying to convince someone on one team or another that they should own the issue.

It would really help if teams on your end collaborated more to discuss feature sets and released guidelines and version "sets" of the various pieces that we are (as consumers) trying to bolt together.
e.g OData + EF seems like a no brainer and yet rarely is.

@TehWardy TehWardy closed this as completed Mar 7, 2020
@TehWardy
Copy link
Author

TehWardy commented Mar 10, 2020

Upgraded as advised, making only the breaking changes required alterations and relevant reference changes, this is what I get ....

image

... 20 minutes later, still going.

If I hit pause in VS to try and identify where it's at it just stops on this ...

using log4net;
using log4net.Config;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Reflection;

namespace Api
{
    public class Program
    {
        public static void Main(string[] args)
        {
            XmlConfigurator.Configure(LogManager.GetRepository(Assembly.GetEntryAssembly()), new FileInfo($"log4net.config"));
            var log = LogManager.GetLogger(typeof(Program));
            try
            {
                log.Info("Starting up ...");
                WebHost.CreateDefaultBuilder(args)
                    .UseKestrel((p) => p.AddServerHeader = false)
                    .ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        config.AddCommandLine(args);
                        config.AddEnvironmentVariables();
                        config.SetBasePath(Directory.GetCurrentDirectory());
                        config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                    })
                    .UseStartup<Startup>()
                    .Build()
                    .Run();
            }
            catch (Exception ex)
            {
                log.Error("Application Failed!", ex);
            }
        }
    }
}

It's already logged information way further down the stack, as far as I can tell the last thing that happened before it went in to this problem was this ...

var page = Db.GetAll<Page>()
    .Include(p => p.PageInfo)
    .Include(p => p.Contents)
    .Include(p => p.Roles)
        .ThenInclude(pr => pr.Role)
    .FirstOrDefault(p => p.AppId == appId && p.Path.ToLower() == path.ToLower());

If I step through the coe the line before it is run (it's a logging call) and the line after it is never hit.
the Db variable here is an instance of an EF context.

Is this usage of EF no longer supported?

This version of the code Is running the latest of everything published on nuget (excluding pre-release versions) for the entire stack.

@TehWardy TehWardy reopened this Mar 10, 2020
@TehWardy
Copy link
Author

Ok it looks like i'm getting deadlocks in the DB for some reason.
Odd because the request in question here does 0 writes to the DB.

@TehWardy
Copy link
Author

turns out the deadlocks were in the CEIP (Customer Experience stuff) so I disabled that then found a more brutal version of the query above that pulled a lot more data.

I think EF Core 2.x used to gen a separate select for each Include and the 33.x version seems to gen a join for includes which triggered the problem (a single 25 minute SQL query execution).
Once I broke the query down it all started clicking in to place.

Now I need to test the original problem (inserts) ...

@TehWardy
Copy link
Author

TehWardy commented Mar 10, 2020

Ok ... same error still happens on EF Core 3.1
It gets worse, existing functionality that used to work now no longerworks because EF breaks it and the advice over on the OData side is to downgrade to EF Core 2.2. ...

OData/WebApi#2015

This is what I meant above when I said ...

It would really help if teams on your end collaborated more to discuss feature sets and released guidelines and version "sets" of the various pieces that we are (as consumers) trying to bolt together.
e.g OData + EF seems like a no brainer and yet rarely is.

@TehWardy
Copy link
Author

Key thing to note here, the advice given above that I could "just upgrade EF" was wrong, upon doing so I found that it has .Net Core 3.1 dependencies so I was forced to upgrade the whole stack.

As well as that issue I'm seeing ...

  • Different behaviour in aspnet request body handling resulting in some requests failing.
  • Simple scalar groupby's are now broken in EF for some reason (all of them across my codebase).
  • Aggregation has all kinds of issues.
  • Query behaviour is different (multiple selects now seem to be aggregated in to joins resulting in bigger single hits on the DB as above).

That's what i've found from just a few hours testing.

The bulk of these issues are workable, not sure how to get round this issue as it seems like a basic function of the framework is at fault.
The groupby problem as @smitpatel has basically put on #17068 that it's basically a rabbit hole you guys can't fix (seems odd since you had a working implementation 1 version ago) so "closed as not needed" for some odd reason. Unless i'm missing something?

@ajcvickers
Copy link
Contributor

@TehWardy So are you trying to make use of temporary values in your code? If so, could you explain why and how you are using them?

@TehWardy
Copy link
Author

Temporary values ?
Sorry I need some context for that as i'm not sure i understand?

@ajcvickers
Copy link
Contributor

@TehWardy I'm going back to the issue you filed, which is about temporary keys being present when they shouldn't be. There's nothing obvious in what you wrote that explains the behavior you're seeing, but we changed some behaviors of temporary values in 3.0, so I was probing as to whether you are using these explicitly.

At this point, I can't see anything that indicates why you're seeing what you are seeing. I think we are going to need code to reproduce this before we can make progress.

@TehWardy
Copy link
Author

This may or may not help, perhaps you can see something I can't about the usage, but this is the entire method in my business logic that handles "the problem scenario" the variable "Db" here is an object that inherits from the EF DbContext class so you can assume that any interaction with that is your territory ...

public override async Task<Page> AddAsync(Page page)
{
    if (!page.PageInfo?.Any() ?? false || page.PageInfo.Any(pi => pi.CultureId == string.Empty))
        throw new InvalidOperationException("Pages MUST have page information defined for the default culture, other cultures are optional.");

    // compute some variables 
    var userIsAppAdmin = User.IsAdminOfApp(page.AppId);
    var userCanCreateChildOfParent = page.ParentId != null ? userIsAppAdmin || UserCan("page_create", (int)page.ParentId) : userIsAppAdmin;

    // grab the parent page
    Page parent = null;
    if (page.ParentId != null)
    {
        Db.DisableFilters();
        parent = Db.GetAll<Page>()
            .Include(p => p.Roles)
            .First(p => p.Id == page.ParentId);
        Db.EnableFilters();
    }

    if (userCanCreateChildOfParent)
    {
        var children = page.Pages;
        page.Pages = null;

        if (page.ParentId != null)
            page.Path = parent.Path + "/" + page.InfoForCulture(string.Empty).Title.Replace(" ", "");
        else
            page.Path = page.InfoForCulture(string.Empty).Title.Replace(" ", "");

        page.Roles = parent?.Roles
            .Select(r => new PageRole { RoleId = r.RoleId })
            .ToArray();

        if (page.Roles == null || !page.Roles.Any())
        {
            if(parent != null)
                page.Roles = parent?.Roles
                    .Select(r => new PageRole { RoleId = r.RoleId })
                    .ToArray();
            else
                page.Roles = User.Roles
                    .Where(r => r.Role.AppId == page.AppId)
                    .Select(ur => new PageRole { RoleId = ur.RoleId })
                    .ToArray();
        }

        // create the page
        var result = await base.AddAsync(page);

        // recurse in to children
        if (children != null && children.Any())
        {
            await Db.SaveChangesAsync();

            foreach (var p in children)
            {
                p.ParentId = result.Id;
                if (p.Id != 0) await UpdateAsync(p); else await AddAsync(p);
            }
        }

        return result;
    }
    else
        throw new SecurityException("Access Denied!");
}

@TehWardy
Copy link
Author

TehWardy commented Mar 16, 2020

My Page entity for use with the above code is as follows.
The entity types UserRole and PageRole are both Standard Join tables as you would expect.
I've also included the User Property from my service so you can see exactly what I pulled from the DB.

// this is the variable "User" consumed in the code, it's a pass through call that just returns Db.User which looks like this ...
private User user = null;
public User User
{
    get
    {
        if (user == null)
        {
            var userName = AuthInfo.Name ?? "Guest";
            user = GetAll<User>(true)
                .Include(u => u.Roles)
                    .ThenInclude(r => r.Role)
                        .ThenInclude(r => r.App)
                .FirstOrDefault(u => u.Id == userName)
                    ??
                new User { DefaultCultureId = string.Empty, IsActive = true, Id = "Guest", DisplayName = "Guest" };

            if (user.Id != AuthInfo.Name)
            {
                ImportUserFromMembership();
                user = GetAll<User>(true)
                    .Include(u => u.Roles)
                        .ThenInclude(r => r.Role)
                            .ThenInclude(r => r.App)
                    .FirstOrDefault(u => u.Id == userName);
            }
        }
        return user;
    }
}


[Table("Pages", Schema = "CMS")]
public class Page
{
    private static readonly ILog log = LogManager.GetLogger(typeof(Page));

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [ForeignKey("Parent")]
    public int? ParentId { get; set; }

    [ForeignKey("App")]
    public int AppId { get; set; }

    [DefaultValue(0)]
    public int Order { get; set; }

    [DefaultValue(true)]
    public bool ShowOnMenus { get; set; }
    public string Path { get; set; }
    public string ResourceKey { get; set; }
    public string Layout { get; set; }
    public virtual App App { get; set; }
    public virtual Page Parent { get; set; }
    public virtual ICollection<PageInfo> PageInfo { get; set; }
    public virtual ICollection<Page> Pages { get; set; }
    public virtual ICollection<Content> Contents { get; set; }
    public virtual ICollection<PageRole> Roles { get; set; }
}

If It helps I can provide the SQL that gets executed up to the problem, but it appears that the issue is caught by something in EF so I figured it's not really a SQL problem.

@ajcvickers
Copy link
Contributor

@TehWardy I spent some time looking through your code and I haven't been able to find anything that immediately seems wrong. Have you looked at and understood the breaking change around temporary values? Since your code is explicitly checking key values it might previously have been relying a non-zero temporary value being present.

@TehWardy
Copy link
Author

That does seem relevant to this code.
In this code I hand off not just a root but some child entities in the creation call on this line ...

var result = await base.AddAsync(page);

... that appears to be the only difference I can see between this code and other similar scenarios that work.

In the other scenarios I would do something like this to "explicitly force EF to be kept in the dark and only do what I asked" ...

var newT = new T() { scalar prop assigns };
newT = Db.Add(newT);   

This would set the primary key value on newT to the EF / Db designated value I would then follow up with something like ...

newT.ChildFoos = new [] {
     new Foo() { scalar prop assigns  },
     new Foo() { scalar prop assigns  }
};

Db.AddAll(newt.ChildFoos);

... in this code I would assign the foreign key value to the Child foo instances to the now known primary key value for it's parent T.

and thus everything would go in.

In the code above I am basically doing something like this ...

var newT() {
     ScalarX = "Y",
     ChildFoos = new [] {
        new Foo() { scalar prop assigns, no PK for parent T  },
        new Foo() { scalar prop assigns, no PK for parent T  }
    }
}

var dbVersionOfTAndFoos = Db.Add(newT);

... is this no longer supported ?
My thinking is that the key handling is entirely an EF problem in this situation since my business logic doesn't care what they are it just wants the entity graph in the DB correctly and wired up.

@ajcvickers
Copy link
Contributor

@TehWardy

is this no longer supported ?

You could still do this, but you’ll need to explicitly get the temporary values from the context entries, as shown in the mitigation for the change.

However, in general an approach that “explicitly force EF to be kept in the dark and only do what I asked” is going to be very fragile. You’ll be constantly working against EF. If it’s not possible to setup an EF model that appropriately specifies relationships such that EF is able to keep these relationships in sync, then it would be good to know about those cases.

My thinking is that the key handling is entirely an EF problem in this situation since my business logic doesn't care what they are it just wants the entity graph in the DB correctly and wired up.

This is why relationships with navigations are used. It removes the need to manage key values explicitly, including in cases where no real key value yet exists, such as in your case.

@TehWardy
Copy link
Author

TehWardy commented Apr 1, 2020

I'm confused by that?
EF is being given an entity and it's children on "some of the relationships" that the entity has and being told "insert this object graph in to the DB" .. i'm then getting this exception.

From what you're saying I have to do something to mitigate EF getting confused about how these entities are related somehow but nothing about this is in my code it's purely an EF problem.

So based on my last post and the last block of code in that post, what should I do to ensure that EF correctly manages the PK values and FK values of the various entities in between the various insert calls it will make to the DB for the row set being added here?

Are you saying that scenario is no longer supported and that what I have to do is break it down like the sample before that?

I'm asking because in this case i'm setting neither the PK, or FK values for any of these entities the entire operation is left to EF to handle however it wants as a single transactional operation on the EF Context API.

@ajcvickers
Copy link
Contributor

what should I do to ensure that EF correctly manages the PK values and FK values of the various entities in between the various insert calls it will make to the DB for the row set being added here

You should not set FK values explicitly.

I'm asking because in this case i'm setting neither the PK, or FK values for any of these entities

From your code above:

p.ParentId = result.Id;

@TehWardy
Copy link
Author

TehWardy commented Apr 7, 2020

So EF doesn't support my scenario any more then?
I can only give it flat sets to store by the sounds of things.

This is a sad day for EF ...

@ajcvickers
Copy link
Contributor

@TehWardy I expect EF does support what you are trying to do, it's just that I don't really understand the specifics of what you're trying to do, and we're talking in generalities because I can't reproduce the behaviors you are seeing. There is likely a bug in either EF or in your code, but since we can't reproduce that bug it's very difficult to give concrete guidance.

@TehWardy
Copy link
Author

TehWardy commented Apr 7, 2020

Literally this ...

var newT() {
     ScalarX = "Y",
     ChildFoos = new [] {
        new Foo() { scalar prop assigns, no PK for parent T  },
        new Foo() { scalar prop assigns, no PK for parent T  }
    }
}

var dbVersionOfTAndFoos = Db.Add(newT);

... although having broken it down to this ...

var newT = new T() { scalar prop assigns };
newT = Db.Add(newT);   
newT.ChildFoos = new [] {
     new Foo() { scalar prop assigns, including setting the now known FK to parent  },
     new Foo() { scalar prop assigns, including setting the now known FK to parent   }
};

Db.AddAll(newt.ChildFoos);

... as per your advice above I've resolved the issue by doing this rewrite.

The problem it seems is that when I submit to an EF context an entity with children it no longer supports inserting both the parent and the children as a single call due to this problem which you're saying is supported but isn't supported at the same time.

I've also been advised to upgrade my EF version by you and to downgrade by the OData team because the current version of EF is broken so it seems no matter what I do EF simply doesn't work anymore.

It's a simple required one to many relationship between Page and Contents and Page and PageInfo (which is where I store the page metadata).

@ajcvickers
Copy link
Contributor

ajcvickers commented Apr 8, 2020

@TehWardy Trying to keep as closely as possible to the code you posted while making something that compiles gives me the following, which works fine. There is something more complex going on in your, and that's what we need to get to the bottom of to be able to solve this.

public static class Program
{
    public static void Main()
    {
        using (var context = new SomeDbContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var newT = new T()
            {
                ScalarX = "Y",
                ChildFoos = new List<Foo>
                {
                    new Foo()
                    {
                        ScalarY = "Z1"
                    },
                    new Foo
                    {
                        ScalarY = "Z2"
                    }
                }
            };

            var dbVersionOfTAndFoos = context.Add(newT);
            
            context.SaveChanges();
        }

        using (var context = new SomeDbContext())
        {
            var t = context.Set<T>().Include(e => e.ChildFoos).First();

            Console.WriteLine(t.ScalarX);
            foreach (var foo in t.ChildFoos)
            {
                Console.WriteLine($"  {foo.ScalarY}");
            }
        }
    }
}

public class T
{
    public int Id { get; set; }
    public string ScalarX { get; set; }
    public ICollection<Foo> ChildFoos { get; set; }
}

public class Foo
{
    public int Id { get; set; }
    public string ScalarY { get; set; }
}


public class SomeDbContext : DbContext
{
    private static readonly ILoggerFactory
        Logger = LoggerFactory.Create(x => x.AddConsole()); //.SetMinimumLevel(LogLevel.Debug));

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<T>();
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseLoggerFactory(Logger)
            .EnableSensitiveDataLogging()
            .UseSqlServer(Your.SqlServerConnectionString);
}

Output

/usr/share/dotnet/dotnet /home/ajcvickers/AllTogetherNow/ThreeOne/bin/Debug/netcoreapp3.1/ThreeOne.dll
warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
      Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data, this mode should only be enabled during development.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 3.1.3 initialized 'SomeDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: SensitiveDataLoggingEnabled 
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (21ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      IF SERVERPROPERTY('EngineEdition') <> 5
      BEGIN
          ALTER DATABASE [Test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
      END;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (51ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      DROP DATABASE [Test];
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (395ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      CREATE DATABASE [Test];
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (130ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      IF SERVERPROPERTY('EngineEdition') <> 5
      BEGIN
          ALTER DATABASE [Test] SET READ_COMMITTED_SNAPSHOT ON;
      END;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE [T] (
          [Id] int NOT NULL IDENTITY,
          [ScalarX] nvarchar(max) NULL,
          CONSTRAINT [PK_T] PRIMARY KEY ([Id])
      );
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE [Foo] (
          [Id] int NOT NULL IDENTITY,
          [ScalarY] nvarchar(max) NULL,
          [TId] int NULL,
          CONSTRAINT [PK_Foo] PRIMARY KEY ([Id]),
          CONSTRAINT [FK_Foo_T_TId] FOREIGN KEY ([TId]) REFERENCES [T] ([Id]) ON DELETE NO ACTION
      );
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX [IX_Foo_TId] ON [Foo] ([TId]);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (24ms) [Parameters=[@p0='Y' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [T] ([ScalarX])
      VALUES (@p0);
      SELECT [Id]
      FROM [T]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (3ms) [Parameters=[@p0='Z1' (Size = 4000), @p1='1' (Nullable = true)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Foo] ([ScalarY], [TId])
      VALUES (@p0, @p1);
      SELECT [Id]
      FROM [Foo]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[@p0='Z2' (Size = 4000), @p1='1' (Nullable = true)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Foo] ([ScalarY], [TId])
      VALUES (@p0, @p1);
      SELECT [Id]
      FROM [Foo]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 3.1.3 initialized 'SomeDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: SensitiveDataLoggingEnabled 
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [t0].[Id], [t0].[ScalarX], [f].[Id], [f].[ScalarY], [f].[TId]
      FROM (
          SELECT TOP(1) [t].[Id], [t].[ScalarX]
          FROM [T] AS [t]
      ) AS [t0]
      LEFT JOIN [Foo] AS [f] ON [t0].[Id] = [f].[TId]
      ORDER BY [t0].[Id], [f].[Id]
Y
  Z1
  Z2

@TehWardy
Copy link
Author

Try updating the entities to something like this (although I admit I can't see how this would affect the outcome) ...

public class T
{
   [Key]
    public int Id { get; set; }
    public string ScalarX { get; set; }
    public ICollection<Foo> ChildFoos { get; set; }
}

public class Foo
{
    [Key]
    public int Id { get; set; }
    [ForeignKey("T")]
    public int TId {  get; set; }
    public string ScalarY { get; set; }
    public virtual T T { get; set; }
}

... or maybe the issue is that the primary keys are all guids?
I have noticed some odd stuff with Guid keys, despite using the DatabaseGenerated attribute it doesn't always set the system up to generate them on the db.

I wonder if it's caused by the parent PK being computed and not given to the child entity set before the add.
I'll check through the history of my code to see if that could be related to my problem.
Or maybe some mix of db generated guids and code generated guids i'm missing in my code.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

2 participants