Как использовать столбец varchar, отличный от Id для ПК?

У меня есть таблица с 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: «Указанное поле «k__BackingField» типа «int» не может использоваться для свойства «Article.Id» типа «string». Можно использовать только резервные поля типов, которые можно назначать из типа свойства.

Я получаю ту же ошибку при запуске Update-Database и Add-Migration.

Обновление 1

@aaron Большое спасибо за вашу помощь. Я попробовал шаги, предложенные вами, но я получаю сообщение об ошибке при обновлении и удалении записей.

Исключение:

ОШИБКА 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; }
}

Обновление 2

Я пытаюсь отключить SoftDelete, обратившись к отключить SoftDelete для AbpUserRole, но он все еще выполняет 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 19: «Ошибка ограничения NOT NULL: Test.Id».




Ответы (2)


Исключением является то, что вы унаследовали FullAuditedEntity<string>, который указывает, что Id относится к типу string, а затем сделали new, чтобы изменить тип на int. Это скрытие приводит к конфликту для EF .

Вот как вы можете:

  1. Иметь автоинкрементный столбец Id типа int
  2. Иметь столбец первичного ключа типа string
  3. Наличие уникального столбца ограничения (как требуется на связанном форуме)

Код:

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;
}

Для полноты картины этот вопрос является первым из серии других вопросов о переполнении стека:

  1. Этот вопрос. (11 сентября)
  2. Получение исключения "Неоднозначное совпадение найдено" при вызове DeleteAsync (11 сентября)
  3. Невозможно удалить запись из таблицы, в которой есть столбец Identity ( 12 сентября)
  4. Как сделать составной уникальный ключ в ASP.NET Boilerplate? (13 сентября)
person aaron    schedule 04.02.2018

Вы имеете в виду использовать тип 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;
person tiennguyen    schedule 20.09.2017