C# 只读标识列的EF Core 2.0.1 AfterSaveBehavior设置为PropertySaveBhavior.Save,而不是PropertySaveBhavior.Throw
我注意到,在EF Core 2.0.1中,一个不是主键的标识列的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 如果没有任何
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
。这里有一个矛盾