如何使用EF核心在SQLite中创建自动增量列?

3vpjnl9f  于 2023-05-18  发布在  SQLite
关注(0)|答案(4)|浏览(213)

我首先在我的UWP and .NET Standard应用程序中使用Entity Framework Core 2.0 for Sqlite代码。我的模型有一个主键整数类型的实体,它应该根据SQLite documentation自动递增。但真实的上,对于每一行,Identity列的值都是0。请帮助,因为我没有找到与此问题相关的帮助材料。
这是我的财产没有任何数据注解。
public Int32 No { get; set; }
我使用了流利的API

modelBuilder.Entity<TurnosGeneral>()
            .HasKey(c => new { c.No, c.Cod_Turno });

这里插入了value

db.TurnosGenerals.Add(new TurnosGeneral { Cod_Turno = numeroTurnoTextBlock.Text });

db.SaveChanges();
对于插入的每一行,c.No为0。

s6fujrry

s6fujrry1#

我的模型有一个主键整型的实体,它应该被用作自动增量
问题是,有问题的属性 * 不是 * 一个PK,而是 * 复合 * PK的 * 一部分,在这种情况下,它不被认为是按照约定自动生成的,如EF Core文档的Generated Values Conventions部分所述:
按照约定,非复合类型的short、int、long或Guid主键将被设置为在add时生成值。将设置所有其他属性,而不生成值。
您需要明确指定:

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

**更新:**以上是适用于大多数数据库的一般方法。但是SQLite支持AutoIncrement only for column of type INTEGER PRIMARY KEY,因此这不是EF Core的限制。要么不使用自动递增,要么非复合主键。

6ojccjat

6ojccjat2#

看起来像个窃听器看看这个https://github.com/aspnet/EntityFrameworkCore/issues/11961
我的解决方法:手动更改:我的DBContext类中Id列上的**. ValueGeneratedNever().ValueGeneratedOnAdd()**。

wwtsj6pe

wwtsj6pe3#

我刚刚在内存数据库中使用SQLite进行测试时遇到了这个问题。在我的例子中,我有一个业务类,其主键字段名为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")行为我修复了错误。

i2loujxw

i2loujxw4#

这是一个非常肮脏的黑客,很大程度上取决于你的要求,显然它不应该在生产中使用。它有一些局限性:例如,所有的插入件必须使用E.F.制造。但也许对某人有用
在我的例子中:我有很多集成测试,我可以使用内存中的Sqlite数据库运行它,有时使用真实的的数据库。为了分离生产代码和测试代码,我将所有代码放在一个新的DbContext中,该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
}

希望能对大家有所帮助:)

相关问题