我首先在我的 UWP 和 .NET Standard 应用程序中使用 Entity Framework Core 2.0 for Sqlite 代码。我的模型有一个主键整数类型的实体,根据 SQLite 文档,它应该作为自动增量。但实际上,对于每一行,标识列的值都为 0。 请帮忙,因为我没有找到与此问题相关的帮助材料。
这是我的财产,没有任何数据注释。
public Int32 No { get; set; }
我用过流利的API
modelBuilder.Entity<TurnosGeneral>()
.HasKey(c => new { c.No, c.Cod_Turno });
在这里插入值
db.TurnosGenerals.Add(new TurnosGeneral { Cod_Turno = numeroTurnoTextBlock.Text });
db.SaveChanges();
对于插入的每一行,c.No 为 0.
我的模型有一个主键整数类型的实体,它应该作为自动增量
问题是所讨论的属性不是 PK,而是复合 PK 的部分,在这种情况下,它不被视为由约定自动生成,如 生成值约定 部分所述EF 核心文档:
按照惯例,非复合 short、int、long 或 Guid 类型的主键将设置为在添加时生成值。所有其他属性都将设置为不产生任何价值。
您需要明确指定:
modelBuilder.Entity<TurnosGeneral>()
.Property(e => e.No)
.ValueGeneratedOnAdd();
更新: 以上是适用于大多数数据库的通用方法。但是 SQLite 仅支持 AutoIncrement 类型为 INTEGER PRIMARY KEY 的列,因此这不是 EF Core 限制。要么不要使用自动增量,要么使其成为非复合 PK。
看起来像一个错误。检查这个:https://github.com/aspnet/EntityFrameworkCore/issues/11961
我的解决方法:在我的 DBContext 类中的 Id 列上手动更改:.ValueGeneratedNever() 到 .ValueGeneratedOnAdd()。
我刚刚在用于测试的内存数据库中遇到了这个问题。就我而言,我有一个商务舱,其主键字段称为
ContactCategoryId
:
public class ContactCategory
{
[Required]
[Display(Name = "Contact category ID")]
public int ContactCategoryId { get; set; }
我也在使用流畅的设置方法:
public void Configure(EntityTypeBuilder<ContactCategory> modelBuilder)
{
modelBuilder.ToTable("CONTACT_CATEGORY", _schema);
modelBuilder.HasKey(x => x.ContactCategoryId);
modelBuilder.Property(x => x.ContactCategoryId)
.HasColumnName(@"ContactCategoryID")
//.HasColumnType("int") Weirdly this was upsetting SQLite
.IsRequired()
.ValueGeneratedOnAdd()
;
注释掉
.HasColumnType("int")
的行为我修复了错误。
这是一个非常肮脏的 hack,很大程度上取决于你的要求,显然它不应该在生产中使用。它有一些局限性:例如,所有插入物都必须使用 E.F 制作……但也许对某些人有用。
在我的例子中:我有很多集成测试,我可以使用内存中的 Sqlite 数据库运行它,有时使用真实的数据库。为了分离生产代码和测试代码,我将所有代码都放在一个新的 DbContext 中,它继承自我的真实 DbContext,在测试中我注入了这个新的 DbContext。
代码:覆盖 SaveChanges 方法并调用您自己的代码。
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
ProcessSqliteIdentities(ChangeTracker.Entries());
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ProcessSqliteIdentities(ChangeTracker.Entries());
return base.SaveChangesAsync(cancellationToken);
}
ProcessSqliteIdentities 函数为身份列设置种子(并保存每个实体的最后一个身份)
//Dictionary to save the last identity for each entity type
private readonly ConcurrentDictionary<Type, int> SqliteIdentities = new();
private void ProcessSqliteIdentities(IEnumerable<EntityEntry> entries)
{
//Just in case
if (Database.ProviderName != "Microsoft.EntityFrameworkCore.Sqlite")
return;
entries
.Where(e => e.State == EntityState.Added)
.Select(e => new
{
e.Entity,
NewIdentity = SqliteIdentities.AddOrUpdate(
e.Entity.GetType(),
1,
(_, currentIdentity) => currentIdentity + 1)
})
.Select(e => e.Entity switch
{
Customer c => c.CustomerId = e.NewIdentity,
Book b => b.BookId = e.NewIdentity,
// Add one line for each of you database types
_ => (object)null! //Just in case you have one entity with no multiple primary keys
})
.ToList(); //Or whatever method you like to force the enumeration of the select
}
我希望它能帮助别人:)