DbSet <TEntity>.Add (TEntity) и уникальность - PullRequest
0 голосов
/ 30 октября 2018

Я просто заново изучаю SQLServer после многих лет использования .Net с другими базами данных.

Я использую Microsoft.EntityFrameworkCore;

У меня есть база данных с 1 таблицей в SQLServer. Из этой таблицы я использовал Entity Framework для генерации модели. Таблица имеет как первичный ключ (автоинкремент), так и уникальный ключ (естественный ключ в трех столбцах таблицы).

Модель, сгенерированная EF, сгенерировала код для индекса уникального ключа:

        entity.HasIndex(e => new { e.Col1, e.Col2, e.Col3})
            .HasName("MyTable_UK")
            .IsUnique();

Я добавляю несколько строк в DbSet, используя: db.MyTable.Add (myRow); без немедленного вызова SaveChanges.

Я ожидал db.MyTable.Add (myRow); бросить исключение, когда уникальный индекс был нарушен. Это не; это позволяет дублировать строки в DbSet. Когда я сохраняю изменения в базе данных, в качестве исключения я получаю нарушение в Великобритании.

Есть ли способ заставить это вызвать исключение, прежде чем я вызову SaveChanges? Кажется, что для этого есть вся необходимая информация.

Ответы [ 2 ]

0 голосов
/ 30 октября 2018
Блок

Обтекание SaveChanges в try...catch всегда является правильным и самым простым способом проверки действий базы данных.

if (ModelState.IsValid)
{
    try
    {
        _context.Add(data);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    catch (DbUpdateException ex)
    {
        if (ex.InnerException is SqlException innerException)
        {
            // handle exception here..
            ModelState.AddModelError("Col1", yourmessage1);
        }
        else
        {
            ModelState.AddModelError("Col1", yourmessage2);
        }
    }
    catch (Exception ex)
    {
        ModelState.AddModelError("Col1", ex.Message);
    }
}
return View();

Но если вы хотите все усложнить, вот правильный путь:

Скажем, вот ваша сущность

public class Example
{
    [Key, Column(Order = 0)]
    public int Col1 { get; set; }

    [Key, Column(Order = 1)]
    public int Col2 { get; set; }

    [Key, Column(Order = 2)]
    public int Col3 { get; set; }

    public string Data { get; set; }
}


public class MyDbContext : DbContext
{
    public virtual DbSet<Example> Examples { get; set; }


    public override int SaveChanges()
    {
        ValidateEntities();
        return base.SaveChanges();
    }

    private void ValidateEntities()
    {
        var hasChanges = ChangeTracker.Entries()
           .Any(x => (x.Entity is Example) && (x.State == EntityState.Added || x.State == EntityState.Modified));

        if (!hasChanges)
        {
            return;
        }

        var addedEntries = ChangeTracker.Entries()
           .Where(x => (x.Entity is Example) && x.State == EntityState.Added)
           .Select(x => x.Entity as Example);

        // The tricky is right here: this.Examples.Where(...), will it execute in DB or local?
        var existingEntities = this.Examples.Where(x => addedEntries.Any(e => e.Col1 == x.Col1 && e.Col2 == x.Col2 && x.Col3 == e.Col3));

        if (existingEntities.Any())
        {
            var keys = string.Join("; ", existingEntities.Select(x => $"{x.Col1}-{x.Col2}-{x.Col3}"));

            throw new Exception($"{keys} already exist.");
        }

        var modifiedEntries = ChangeTracker.Entries()
            .Where(x => (x.Entity is Brand) && x.State == EntityState.Modified);

        if (modifiedEntries.Any())
        {
            return;
        }

        ////TO DO: the rest code for modified entries, more complex than added part.
    }
}
0 голосов
/ 30 октября 2018

Вы можете легко написать запрос, чтобы проверить, существует ли запись, которую вы пытаетесь вставить, уже существует:

if (!db.MyTable.Any(e => e.Col1 == myRow.Col1 && e.Col2 == myRow.Col2 && e.Col3 == myRow.Col3))
{
     db.MyTable.Add(myRow);
}
else {
    // You can throw an exception here if you'd like but I usually prefer to return 'false' or some other indicator.
}
...