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

时间:2017-09-11 10:22:44

标签: c# entity-framework-core primary-key aspnetboilerplate asp.net-boilerplate

我有一个以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:&#39;指定字段&#39; k__BackingField&#39;类型&#39; int&#39;不能用于财产&#39; Article.Id&#39;类型&#39;字符串&#39;。只能使用可从属性类型分配的类型的后备字段。&#39;

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

更新1

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

例外:

  

ERROR 2018-02-12 06:13:23,049 [30]   Mvc.ExceptionHandling.AbpExceptionFilter - 发生错误   更新条目。有关详细信息,请参阅内部异常   Microsoft.EntityFrameworkCore.DbUpdateException:发生错误   在更新条目时。有关详细信息,请参阅内部异常---&GT;   System.Data.SqlClient.SqlException:无法更新标识列   &#39;标识&#39;

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

我试图通过引用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:&#39; NOT NULL约束失败:Test.Id&#39;。

2 个答案:

答案 0 :(得分:1)

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

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

然后你可以使用repository:

private readonly IRepository<Article, string> _articleRepository;

答案 1 :(得分:1)

例外是因为您继承了FullAuditedEntity<string>,指定Id的类型为string,然后new将类型更改为int 。此hiding会导致EF发生冲突。

您可以在这里:

  1. 拥有Id
  2. 类型的自动增加int
  3. 拥有string
  4. 类型的主键列
  5. 拥有唯一约束列(根据related forum中的要求)
  6. 代码:

    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日)