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

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

我有一个表有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:''int'类型的指定字段'k__BackingField'不能用于'string'类型的属性'Article.Id'。只能使用可从属性类型分配的类型的备份字段。

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

Update 1

@aaron非常感谢你的帮助。我已尝试过您建议的步骤,但在更新和删除记录时遇到错误。

例外:

ERROR 2018-02-12 06:13:23,049 [30] Mvc.ExceptionHandling.AbpExceptionFilter - 更新条目时发生错误。有关详细信息,请参阅内部异常Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时发生错误。有关详细信息,请参阅内部异常---> System.Data.SqlClient.SqlException:无法更新标识列'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; }
}

Update 2

我试图通过引用Disable SoftDelete for AbpUserRole来禁用SoftDelete,但它仍在使用SoftDelete,而不是从DB中删除行。请找截图:

here

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错误19:'NOT NULL约束失败:Test.Id'。

c# entity-framework-core primary-key aspnetboilerplate asp.net-boilerplate
2个回答
1
投票

唯一的例外是因为你继承了FullAuditedEntity<string>,它指定Idstring类型,然后new将类型更改为int。这个hiding导致EF的冲突。

以下是您的方法:

  1. 有一个Id类型的自动增量int
  2. 有一个string类型的主键列
  3. 有一个唯一的约束列(根据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. Getting Ambiguous match found exception while calling DeleteAsync(9月11日)
  3. Cannot delete record from table which has Identity column(9月12日)
  4. How to make composite unique key in ASP.NET Boilerplate?(9月13日)

1
投票

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

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;
© www.soinside.com 2019 - 2024. All rights reserved.