DbSet <tentity> .Add(TEntity)和唯一性

时间:2018-10-30 12:58:21

标签: c# .net sql-server entity-framework-core

在将.Net与其他数据库一起使用多年之后,我只是在重新学习SQLServer。

我正在使用Microsoft.EntityFrameworkCore;

我在SQLServer中有一个1表数据库。从该表中,我已使用实体框架来生成模型。该表具有主键(自动递增)和唯一键(表的三列上的自然键)。

由EF生成的模型已为唯一键索引生成了代码:

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

我正在使用db.MyTable.Add(myRow);向DbSet添加多行。无需立即调用SaveChanges。

我期待db.MyTable.Add(myRow);违反唯一索引时引发异常。它不是;它允许DbSet中有重复的行。当我将更改保存到数据库时,我得到了英国违规作为例外。

在调用SaveChanges之前,有什么方法可以使它引发异常吗?它似乎具有执行此操作所需的所有信息。

2 个答案:

答案 0 :(得分:2)

您可以轻松地编写查询来检查您要插入的记录是否已经存在:

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

答案 1 :(得分:1)

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