如何使用 EF core 在 SQLite 中创建自增列?

问题描述 投票:0回答:4

我首先在我的 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.

c# sqlite entity-framework-core auto-increment .net-standard-2.0
4个回答
25
投票

我的模型有一个主键整数类型的实体,它应该作为自动增量

问题是所讨论的属性不是 PK,而是复合 PK 的部分,在这种情况下,它不被视为由约定自动生成,如 生成值约定 部分所述EF 核心文档:

按照惯例,非复合 short、int、long 或 Guid 类型的主键将设置为在添加时生成值。所有其他属性都将设置为不产生任何价值。

您需要明确指定:

modelBuilder.Entity<TurnosGeneral>()
    .Property(e => e.No)
    .ValueGeneratedOnAdd();

更新: 以上是适用于大多数数据库的通用方法。但是 SQLite 仅支持 AutoIncrement 类型为 INTEGER PRIMARY KEY 的列,因此这不是 EF Core 限制。要么不要使用自动增量,要么使其成为非复合 PK。


4
投票

看起来像一个错误。检查这个:https://github.com/aspnet/EntityFrameworkCore/issues/11961

我的解决方法:在我的 DBContext 类中的 Id 列上手动更改:.ValueGeneratedNever().ValueGeneratedOnAdd()


4
投票

我刚刚在用于测试的内存数据库中遇到了这个问题。就我而言,我有一个商务舱,其主键字段称为

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")
的行为我修复了错误。


0
投票

这是一个非常肮脏的 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
}

我希望它能帮助别人:)

© www.soinside.com 2019 - 2024. All rights reserved.