C# 只读标识列的EF Core 2.0.1 AfterSaveBehavior设置为PropertySaveBhavior.Save,而不是PropertySaveBhavior.Throw

C# 只读标识列的EF Core 2.0.1 AfterSaveBehavior设置为PropertySaveBhavior.Save,而不是PropertySaveBhavior.Throw,c#,sql-server,unit-testing,ef-core-2.0,C#,Sql Server,Unit Testing,Ef Core 2.0,我注意到,在EF Core 2.0.1中,一个不是主键的标识列的IProperty.AfterSaveBehavior等于PropertySaveBhavior.Save,尽管它具有只读行为。换句话说,保存行为之后意味着可以修改标识列,但如果我们尝试修改标识列,则会收到一个DbUpdateException 但是,如果我们在修改数据库中现有记录的标识列时收到运行时异常,则IProperty.AfterSaveBehavior不应等于propertySaveBhavior.Throw 如果没有任何

我注意到,在EF Core 2.0.1中,一个不是主键的标识列的
IProperty.AfterSaveBehavior
等于
PropertySaveBhavior.Save
,尽管它具有只读行为。换句话说,保存行为之后意味着可以修改标识列,但如果我们尝试修改标识列,则会收到一个
DbUpdateException

但是,如果我们在修改数据库中现有记录的标识列时收到运行时异常,则
IProperty.AfterSaveBehavior
不应等于
propertySaveBhavior.Throw

如果没有任何书面代码,我提到的可能不是很清楚。因此,我创建了一个小单元测试作为概念验证。我通过选择.NET Core->xunit测试项目(.NET Core),在Visual Studio 2017中创建了一个xunit测试

为了执行单元测试,我们必须首先通过在PackageManager控制台中执行以下命令来安装EF Core 2.0.1和MSSQL提供程序

Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Add-Migration migration_name
Update-Database
Xunit测试的源代码是:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Linq;
using System.Reflection;
using Xunit;


namespace EFCoreIdentityColumn
{
    public class DataRecord
    {
        public int ID { get; set; }
        public int IdentityField { get; set; }
    }

    class IdentityContext : DbContext
    {
        public IdentityContext() { }
        public IdentityContext(DbContextOptions<IdentityContext> options) : base(options) { }
        public DbSet<DataRecord> DataRecords { get; set; }


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string conn = "Server=(localdb)\\mssqllocaldb;Database=TESTIDENTITY;Trusted_Connection=True;MultipleActiveResultSets=true";
            optionsBuilder.UseSqlServer(conn);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<DataRecord>()
                .Property(i => i.ID)
                .ValueGeneratedNever();

            modelBuilder.Entity<DataRecord>()
                .Property(i => i.IdentityField)
                .UseSqlServerIdentityColumn();
        }
    }


    public class IdentityColumnXunitTest
    {
        [Fact]
        public void IdentitySaveBehaviorWithMSSQL()
        {
            using (var context = new IdentityContext())
            {
                //examine DataRecord.IdentityField property
                var identityPropInfo = typeof(DataRecord).GetTypeInfo().DeclaredProperties.First(p => p.Name == "IdentityField");
                var identityIProperty = context.Model.FindEntityType(typeof(DataRecord)).FindProperty(identityPropInfo);
                Assert.Equal(ValueGenerated.OnAdd, identityIProperty.ValueGenerated);
                Assert.Equal(PropertySaveBehavior.Save, identityIProperty.BeforeSaveBehavior);
                Assert.Equal(PropertySaveBehavior.Save, identityIProperty.AfterSaveBehavior);
                Assert.False(identityIProperty.IsReadOnlyAfterSave);
                Assert.True(identityIProperty.SqlServer().ValueGenerationStrategy
                    == SqlServerValueGenerationStrategy.IdentityColumn);
                Assert.False(identityIProperty.IsNullable);
            }
        }

        [Fact]
        public void IdentityColumnSaveBehaviorWithMSSQL()
        {
            using (var context = new IdentityContext())
            {
                context.Database.ExecuteSqlCommand("TRUNCATE TABLE DataRecords");

                DataRecord rec1 = new DataRecord();
                var entry1 = context.Add(rec1);
                Assert.Equal(EntityState.Added, entry1.State);
                Assert.Equal(1, context.SaveChanges()); //one record was added
                Assert.Equal(0, context.DataRecords.First().ID);
                Assert.Equal(1, context.DataRecords.First().IdentityField);

                //primary key value is readonly after save and cannot be modified
                rec1.ID = 10;
                Assert.Throws<InvalidOperationException>(() => context.SaveChanges());

            }

            using (var context = new IdentityContext())
            {
                //we cannot supply a value for identity column
                DataRecord rec2 = new DataRecord()
                {
                    ID = 323,
                    IdentityField = 5
                };

                var entry2 = context.Add(rec2);
                Assert.Equal(EntityState.Added, entry2.State);
                Assert.Throws<DbUpdateException>(() => context.SaveChanges());
            }

            using (var context = new IdentityContext())
            {
                DataRecord rec = new DataRecord()
                {
                    ID = 299,
                };

                var entry = context.Add(rec);
                Assert.Equal(EntityState.Added, entry.State);
                Assert.Equal(1, context.SaveChanges()); //one record was added
                Assert.Equal(299, context.DataRecords.First(i => i.ID == rec.ID).ID);
                Assert.Equal(2, context.DataRecords.First(i => i.ID == rec.ID).IdentityField);

                //we cannot update identity column
                Assert.True(context.Entry(rec).State == EntityState.Unchanged);
                rec.IdentityField = 544;
                Assert.True(context.Entry(rec).State == EntityState.Modified);
                Assert.Throws<DbUpdateException>(() => context.SaveChanges());
            }
        }

    }
}
测试确认,尽管实际上标识列是只读的,但
AfterSaveBehavior
设置为
propertySavebhavior.Save
IsReadOnlyAfterSave
设置为
false
。这里有一个矛盾