如何使用entityframework阻止从db中检索重复值

时间:2017-09-11 10:43:14

标签: c# entity-framework entity-framework-6

我有一种情况,我在名为Profile的表中有一个计数器字段,在表单提交时,我将检索计数器字段和+1到计数器和更新配置文件表。增量计数器将存储在一个变量中,然后我将用于在另一个表[Bidder]中创建新记录。问题是当同时提交多个表单时,将在Bidder表中创建重复的记录值

Profile profile = db.Profile.Where(w => w.TenderId == tender_Id && w.IsDeleted == false).FirstOrDefault();
int submission = profile.TotalSubmission + 1;

if (profile != null) {
    profile.TotalSubmission = submission;
    profile.ModifiedBy = user_id;
    profile.ModifiedOn = DateTime.Now;
    db.Entry(profile).State = EntityState.Modified;
    db.SaveChanges();
}

bid.ROId = string.Format("RO{0}", submission);
db.Entry(bid).State = EntityState.Modified;
db.SaveChanges();

如何防止创建重复的ROId?

3 个答案:

答案 0 :(得分:0)

您不能仅依赖实体框架来解决问题。只有数据库才能全面了解存储的数据。您的不同实体上下文实例甚至不知道是否存在其他实例,因此在EF级别上协调全局范围内的序列号非常困难。

根据冲突的频率,我想到两个选项来强制执行序列号的唯一性:

  1. 唯一约束
  2. 写入数据的存储过程
  3. 唯一约束

    您可以在UNIQUEProfileId列上创建Sequence约束。使用重复的序列号存储数据时,您将收到异常。异常本身或其内部异常之一将是SqlException。您可以检查该异常的错误编号,如果它是error number 2627(如果您的DBMS是SQL Server;如果不是,请检查DBMS中的类似错误),您知道这是一个唯一的密钥约束违规。在这种情况下,您将从DB获取当前序列号,并使用新序列再次写入数据。你必须重复这一点,直到插入成功。

    如果您使用的是SQL Server,则可以选择性地处理UNIQUE KEY这样的约束违规(使用C#6.0异常过滤器):

    private bool IsUniqueKeyViolation(Exception exception) {
        Exception currentException = exception;
        while (currentException != null) {
            SqlException sqlException = exception as SqlException;
            if (sqlException != null) {
                return sqlException.Errors.Cast<SqlError>().Any(error => error.Number == 2627);
            }
            currentException = currentException.InnerException;
        }
        return false;
    }
    
    //...
    
    //...Code to set up the POCOs before Save...
    while(true) {
        try {
            context.SaveChanges();
        }
        catch(Exception exc) when (IsUniqueKeyViolation(exc)) {
            //...Code to update the sequence number...
            continue;
        }
        break;
    }
    

    此解决方案仅在预计冲突数量较小时才可行。如果冲突的数量很大,您将看到许多对数据库的UPDATE请求失败,这可能会成为性能问题。

    编辑:

    正如其他一些答案所建议的那样,您还可以将乐观并发与时间戳列一起使用。只要您只从自己的代码更新数据库,这就可以正常工作。但是,UNIQUE KEY约束将保护数据的完整性,同时保护数据不会来自不是来自应用程序的更改(如迁移脚本等)。乐观并发不会给你相同的保证。

    存储过程

    您可以创建一个存储过程,该存储过程将从相同的 INSERTUPDATE语句中的最后一个现有数字设置新的序列号。存储过程可以将新的序列号返回给客户端,您可以相应地处理它。

    由于此解决方案将始终在单个语句中更新数据库,因此适用于大量冲突更新。缺点是您必须在DB级别的SQL中编写程序逻辑的一部分。

答案 1 :(得分:0)

应使用唯一索引或唯一约束来强制执行唯一性。

您可以先使用代码创建这些代码(来自MSDN):

public class User
{
    public int UserId { get; set; }

    [Index(IsUnique = true)]
    public string Username { get; set; }

    public string DisplayName { get; set; }
}

或直接通过数据库。

应使用乐观并发来保护计数器:

public class MyEntity
{
    [Key]
    public Guid Id { get; set; }

    // Add a timestamp property to your class
    [Timestamp]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    [ConcurrencyCheck]
    public byte[] VersionTimestamp { get; set; }

    public int Counter { get; set; }
}

如果您尝试使用VersionTimestamp更新行,而不是从数据库中重新读取,那么您将获得OptimisiticConcurrencyException,例如// Read the entity MyEntity entity; using (var context = new MyContext()) { entity = context.MyEntities.Single(e => e.Id == id1); } // Read and update the entity using (var context = new MyContext()) { var entity2 = context.MyEntities.Single(e => e.Id == id1); entity2.Counter++; context.SaveChanges(); } // Try to update stale data // - an OptimisticConcurrencyException will be thrown using (var context = new MyContext()) { entity.Counter++; context.SaveChanges(); } 。在这个测试场景中:

String randomPass = passwordEncoder.generateRand()

答案 2 :(得分:0)

如果您使用的是SQL Server 2012或更高版本,则可以使用Sequence来完成此操作。您还希望通过唯一约束强制实现唯一性。

public partial class YourEfContext : DbContext 
{
    .... (other EF stuff) ......

    // get your EF context
    public int GetNextSequenceValue()
    {
        var rawQuery = Database.SqlQuery<int>("SELECT NEXT VALUE FOR dbo.SomeSequence;");
        var task = rawQuery.SingleAsync();
        int nextVal = task.Result;

        return nextVal;
    }
}

如果您没有支持序列的版本,另一个选择是使用数据库上的存储过程来发出Id编号。存储过程可以与ID表一起使用,它可以显式锁定。这意味着你可以从proc请求一个id,它可以锁定表,读取当前的数字,递增它,将它存储回表中,释放锁,然后返回id。您需要从代码中调用proc以获取要分配的新ID。数据库端的锁定确保您只被分配了唯一值。只要您的id列只被赋予proc指定的值,您将拥有唯一值。您仍然可以分配任意数字,可以包括重复项,可以使用唯一约束来解决。

在Entity-Framework中没有特定的,尽管你仍然可以通过某种方式通过实体框架访问所有这些。