Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

NET 5.0 RC1 Table-per-type (TPT): DbUpdateException in SaveChanges when inserting objects with Foreign Key relationship #22577

Closed
tivtag opened this issue Sep 17, 2020 · 2 comments
Labels
area-save-changes closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@tivtag
Copy link

tivtag commented Sep 17, 2020

Trying out the new Table-per-type (TPT) mapping feature, I get the following DbUpdateException with a FK-Error in SaveChanges, when I:

  • create a type hierachy Animal > Pet.
  • Pet references PetFood (see property FavouritePetFood).
  • Insert both a PetFood and a Pet, that contains the PetFood.

The exception occurs in 5.0.0-rc.1.20451.13 and 6.0.0-alpha.1.20465.1.
All works fine with 5.0.0-preview.8.20407.4!

(Removing the usage of the TPT-Feature, also makes the example work.)

Exception:

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904):
Die INSERT-Anweisung steht in Konflikt mit der FOREIGN KEY-Einschränkung \"FK_Pets_PetFoods_FavouritePetFood_Id\". 
Der Konflikt trat in der TestDB_EfBug_FK-Datenbank, Tabelle \"dbo.PetFoods\", column 'PetFoods_Id' auf.
Die Anweisung wurde beendet.
  at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
  at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
 at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
 at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method)
  at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
  at Microsoft.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)\
   at System.Data.Common.DbCommand.ExecuteReader()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
  at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)\r\nClientConnectionId:4d09870f-da62-4915-abea-23a8535e776a\r\nError Number:547,State:0,Class:16
  --- End of inner exception stack trace ---
  at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
  at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
  at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
  at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
  at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)
  at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementation[TState,TResult](Func`3 operation, Func`3 verifySucceeded, TState state)
  at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
  at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
  at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
  at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()\r\n   at EfcBug.Program.Main(String[] _)

Steps to reproduce:

namespace EfcBug
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.EntityFrameworkCore;

    [Table( "Animals" )]
    public class Animal
    {
        [Key]
        [DatabaseGenerated( DatabaseGeneratedOption.Identity )]
        public int Id { get; set; }

        public string Species { get; set; }
    }

    [Table( "Pets" )]
    public class Pet : Animal
    {
        public string Name { get; set; }

        [Column( "FavouritePetFood_Id" )]
        [ForeignKey( nameof( FavouritePetFood ) )]
        public int? FavouritePetFoodId { get; set; }
        public PetFood FavouritePetFood { get; set; }
    }

    [Table( "PetFoods" )]
    public sealed class PetFood
    {
        [Key]
        [DatabaseGenerated( DatabaseGeneratedOption.Identity )]
        [Column( "PetFoods_Id" )]
        public int PetFoodId { get; set; }

        public string FoodName { get; set; }
    }

    public class EfBugDbContext : DbContext
    {
        protected override void OnConfiguring( DbContextOptionsBuilder options )
        {
            options
                .UseSqlServer(
                    "Data Source=localhost;Initial Catalog=TestDB_EfBug_FK;Persist Security Info=True;User ID=User;Password=Pass",
                    providerOptions => providerOptions.EnableRetryOnFailure() )
                .EnableDetailedErrors()
                .EnableSensitiveDataLogging();
        }

        protected override void OnModelCreating( ModelBuilder modelBuilder )
        {
            modelBuilder.Entity<Animal>().ToTable( "Animals" );
            modelBuilder.Entity<Pet>().ToTable( "Pets" ).HasOne( x => x.FavouritePetFood );
            modelBuilder.Entity<PetFood>().ToTable( "PetFoods" );
        }
    }

    public class Program
    {
        static void Main( string[] _ )
        {
            try
            {
                using( EfBugDbContext db = new EfBugDbContext() )
                {
                    db.Database.EnsureCreated();
                }

                using( EfBugDbContext db = new EfBugDbContext() )
                {
                    var petFood = new PetFood() { FoodName = "Fish" };
                    db.Add( petFood );

                    var pet = new Pet() { Name = "Kittykat", FavouritePetFood = petFood };
                    db.Add( pet );

                    db.SaveChanges();
                }

                Console.WriteLine( "OK" );
            }
            catch( Exception ex )
            {
                Console.WriteLine( ex.ToString() );
            }

            Console.ReadLine();
        }
    }
}
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Configurations>Debug;Release;TestRelease;ReleaseKundeProd;ReleaseKundeTest</Configurations>
  </PropertyGroup>
  
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <RootNamespace>EfcBug</RootNamespace>
    <AssemblyName>EfcBug</AssemblyName>
  </PropertyGroup>

  <PropertyGroup>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <LangVersion>latest</LangVersion>
    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
    <Prefer32Bit>false</Prefer32Bit>
    <StartupObject>EfcBug.Program</StartupObject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0-alpha.1.20465.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0-alpha.1.20465.1">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="6.0.0-alpha.1.20465.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0-alpha.1.20465.1" />
  </ItemGroup>

  <!--<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0-rc.1.20451.13" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0-rc.1.20451.13">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="5.0.0-rc.1.20451.13" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-rc.1.20451.13" />
  </ItemGroup>-->

  <!--<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.0-preview.8.20407.4" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0-preview.8.20407.4">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="5.0.0-preview.8.20407.4" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-preview.8.20407.4" />    
  </ItemGroup>-->

</Project>

Further technical details

EF Core version: 5.0.0-rc.1.20451.13 (doesn't work), 6.0.0-alpha.1.20465.1 (doesn't work), 5.0.0-preview.8.20407.4 (works fine!)
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.1.8
Operating system: Windows 10 2004
SQL Server 15.0.4063.15
IDE: Visual Studio 2019 16.7.3

@AndriySvyryd
Copy link
Member

Looks like the root cause is the same as in #22573

@AndriySvyryd
Copy link
Member

There is an additional issue in value propagation

@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 18, 2020
@AndriySvyryd AndriySvyryd added this to the 5.0.0-rc2 milestone Sep 18, 2020
@AndriySvyryd AndriySvyryd removed their assignment Sep 18, 2020
@ajcvickers ajcvickers modified the milestones: 5.0.0-rc2, 5.0.0 Nov 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-save-changes closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
Development

No branches or pull requests

3 participants