如何使用 Id 以外的 varchar 列进行 PK?

How to use a varchar column other than Id for PK?

我有一个 table,它有 Code 作为 PK,但是我在 DefaultEditionCreator.cs 中得到了下面的异常,一旦我尝试 运行 应用程序。

[Table("Test")]
public class Test: FullAuditedEntity<string>
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    new public int Id { get; set; }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    [MaxLength(NVarcharLength14), DataType(DataType.Text)]
    public virtual string Code { get; set; }
}

声明的存储库:

private readonly IRepository<Article, string> _articleRepository;

异常:

System.InvalidOperationException: 'The specified field 'k__BackingField' of type 'int' cannot be used for the property 'Article.Id' of type 'string'. Only backing fields of types that are assignable from the property type can be used.'

我在 运行宁 Update-DatabaseAdd-Migration 时遇到同样的错误。

更新 1

@aaron 非常感谢您的帮助。我已经尝试了您建议的步骤,但是在更新和删除记录时出现错误。

异常:

ERROR 2018-02-12 06:13:23,049 [30 ] Mvc.ExceptionHandling.AbpExceptionFilter - An error occurred while updating the entries. See the inner exception for details. Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Cannot update identity column 'Id'.

public async Task UpdateTest()
{
   var entity = GetAll().Where(x => x.TestId == "One").FirstOrDefault();
   await UpdateAsync(entity);
}

public async Task DeleteTest()
{
   await DeleteAsync(x => x.TestId == "One"); 
}

public class Test : FullAuditedEntity
{
   // PK
   public string TestId { get; set; }

   // Unique constraint
   public int TestId2 { get; set; }
}

更新 2

我试图通过参考 来禁用 SoftDelete,但它仍在执行 SoftDelete,而不是从数据库中删除行。请找截图:

public class TestAppService : MyProjectAppServiceBase, ITestAppService
{
    public Task DeleteTest()
    {
        using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.SoftDelete))
        {
            return _testRepository.DeleteTest();
        }
    }
}

MyDBContext.cs:

protected override void CancelDeletionForSoftDelete(EntityEntry entry)
{
    if (IsSoftDeleteFilterEnabled)
    {
        base.CancelDeletionForSoftDelete(entry);
    }
}

解决方案工作正常,但在 运行 测试用例创建 Test 实体时出现以下异常。

SQLite Error 19: 'NOT NULL constraint failed: Test.Id'.

你的意思是使用varchar类型作为主键?只需像这样声明实体 class:

public class Article: Entity<string>
{
  //You should comment this line
  //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  //new public int Id { get; set; }
}

然后你可以使用存储库:

private readonly IRepository<Article, string> _articleRepository;

异常是因为你继承了FullAuditedEntity<string>,指定Idstring类型,然后new把类型改成了[=17] =].这 导致 EF 冲突。

您可以这样做:

  1. 有一个类型为 int
  2. 的自动递增 Id
  3. 有一个 string
  4. 类型的主键列
  5. 具有唯一约束列(如 related forum 中所要求)

代码:

public class Test: FullAuditedEntity
{
    // PK
    [MaxLength(NVarcharLength14), DataType(DataType.Text)]
    public virtual string Code { get; set; }

    // Unique constraint
    public int MyUniqueId { get; set; }
}

public class AbpProjectNameDbContext : AbpZeroDbContext<Tenant, Role, User, AbpProjectNameDbContext>
{
    /* Define a DbSet for each entity of the application */    
    public DbSet<Test> Tests { get; set; }

    public AbpProjectNameDbContext(DbContextOptions<AbpProjectNameDbContext> options) : base(options) {}

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Test>().Property(t => t.Id).ValueGeneratedOnAdd(); // Auto-increment
        modelBuilder.Entity<Test>().HasAlternateKey(t => t.Id);                // Auto-increment, closed-wont-fix: https://github.com/aspnet/EntityFrameworkCore/issues/7380
        modelBuilder.Entity<Test>().HasKey(t => t.Code);                       // PK
        modelBuilder.Entity<Test>().HasIndex(t => t.MyUniqueId).IsUnique();    // Unique constraint
    }
}

生成的迁移:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Tests",
        columns: table => new
        {
            Code = table.Column<string>(nullable: false),
            Id = table.Column<int>(nullable: false)
                .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
            MyUniqueId = table.Column<int>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Tests", x => x.Code);
        });

    migrationBuilder.CreateIndex(
        name: "IX_Tests_MyUniqueId",
        table: "Tests",
        column: "MyUniqueId",
        unique: true);
}

用法:

public async Task MyMethod()
{
    await _repository.InsertAndGetIdAsync(new Test
    {
        Code = "One",
        MyUniqueId = 1
    });

    // Valid
    await _repository.InsertAndGetIdAsync(new Test
    {
        Code = "Two",
        MyUniqueId = 2
    });

    try
    {
        await _repository.InsertAndGetIdAsync(new Test
        {
            Code = "One", // PK conflict
            MyUniqueId = 3
        });
    }
    catch (Exception e)
    {
    }

    try
    {
        await _repository.InsertAndGetIdAsync(new Test
        {
            Code = "Three",
            MyUniqueId = 1 // Unique constraint conflict
        });
    }
    catch (Exception e)
    {
        throw;
    }

    return null;
}

为了完整起见,这个问题是一系列其他 Stack Overflow 问题中的第一个:

  1. 这个问题。 (9 月 11 日)
  2. (9 月 11 日)
  3. (9 月 12 日)
  4. (9 月 13 日)