17

目前正在使用 Entity Framework 5 开发 ASP.Net MVC 4 应用程序。在初始开发阶段使用 CodeFirst。但是现在已经禁用了自动迁移并直接使用 SSMS 和编写 POCO 设计新表。一切运作良好。

最近,在生产中发现了一个奇怪的问题。最初设计的一个表中的记录跳过了 900 多个数字的自动递增标识值。这在过去 3 个月内发生了 3 次。在本地调试应用程序但无法重现。没有观察到任何模式或趋势。

模型:

public class Enquiry
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public Int64 EnquiryId { get; set; }

    [Required]
    public int UserId { get; set; }

    [Required]
    public byte Bid { get; set; }

    ...

    [Required]
    public DateTime Created { get; set; }

    [Required]
    public DateTime Modified { get; set; }
}

public class EnquiryDetail
{
    [Key]
    public Int64 EnquiryId { get; set; }

    [Required]
    public int CreditScore { get; set; }

    [Required]
    public byte BidMode { get; set; }

    public virtual Enquiry Enquiry { get; set; }
}

数据库上下文:

public class EscrowDb : DbContext
{

    public EscrowDb()
        : base("name=DefaultConnection")
    {

    }
    public DbSet<Enquiry> Enquiries { get; set; }
    public DbSet<EnquiryDetail> EnquiryDetails { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Entity<EnquiryDetail>()
            .HasRequired<Enquiry>(ed => ed.Enquiry)
            .WithRequiredDependent(e => e.EnquiryDetail);
    }
}

控制器:

[Authorize]
public class EnquiryController : Controller
{
    private EscrowDb _db = new EscrowDb();

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(EnquiryViewModel core)
    {
       var enquiry = new Enquiry();
       // Some code to set properties using passed ViewModel
       ...

       var enquiryDetail = new EnquiryDetail();
       // Some code to set properties using passed ViewModel
       ...

       enquiry.EnquiryDetail = enquiryDetail;

       _db.Enquiries.Add(enquiry);
       _db.SaveChanges();
    }
}

到目前为止,所有这些代码都运行良好,除了身份值偶尔会被近 1000 个数字的大间隙跳过。

有没有人遇到过这样的问题?请分享你的想法。

4

4 回答 4

19

如果您需要消除这些差距,您可能会不走运。

我在开发/测试新应用程序时自己遇到了这个问题。根据我所阅读的有关 sql server 2012 的内容,我可以直观地了解 sql azure 中发生的情况。我无法找到有关 sql azure 的任何文档。

从我读过的内容来看,这是一个作为错误 IMO 出现的功能。在 Sql server 2012 中,Microsoft 添加了创建序列的功能。序列记录在 1000 个块中使用了哪些值。因此,假设您的序列正在进行... 1、2、3、4、5...,然后您的 sql server 重新启动。好吧,序列已经保存了块 1-1000 已经被使用的事实,因此它会将您跳转到下一个 1000....所以您的下一个值是 1001、1002、1003、1004...。这提高了使用序列时插入,但可能导致不寻常的间隙。您的序列有一个解决方案。指定序列时,添加“NOCACHE”参数,这样它就不会一次保存 1000 个块。有关更多文档,请参见此处。

这成为一个问题的地方是 Identity 列似乎已更改为使用相同的范例。因此,当您的服务器或在这种情况下您的 sql azure 实例重新启动时,您的身份列中可能会出现较大的间隙(1000 个),因为它将大块缓存为“已使用”。sql server 2012 有一个解决方案。您可以指定启动标志 t272 以将您的身份恢复为使用旧的 sql server 2008 r2 范例。问题是我不知道(可能不可能)如何在 sql Azure 中指定它。找不到文档。 有关 sql server 2012 的更多详细信息,请参阅此线程。

在 msdn 中查看身份文档。特别是“服务器重新启动或其他故障后的连续值”部分。这是它所说的:

服务器重新启动或其他故障后的连续值- SQL Server 可能出于性能原因缓存标识值,并且在数据库故障或服务器重新启动期间,某些分配的值可能会丢失。这可能会导致插入时标识值出现间隙。如果间隙不可接受,则应用程序应使用带有 NOCACHE 选项的序列生成器或使用它们自己的机制来生成键值。

因此,如果您需要连续的值,您可以尝试使用 nocache 指定一个序列,而不是依赖于您的标识列。我自己没有尝试过,但听起来你很难让它与实体框架一起使用。

抱歉,如果这没有太大帮助,但至少它是关于您的体验的一些信息。

于 2013-07-10T14:00:06.967 回答
1

尝试使用触发方法重新播种。我相信这应该解决它的使用示例,并在该链接上查看更多解决方法。

USE [TEST]

CREATE TABLE TEST(ID INT IDENTITY(1,1),VAL VARCHAR(10))

CREATE TRIGGER TGR_TEST_IDENTITY ON TEST
FOR INSERT
AS
DECLARE @RESEEDVAL INT
SELECT @RESEEDVAL = MAX(ID) FROM TEST
DBCC CHECKIDENT('TEST', RESEED, @RESEEDVAL)

INSERT INTO TEST(VAL)VALUES('a')

SELECT * FROM TEST

由于 Azure 现在不支持“DBCC CHECKIDENT”,因此您可以使用此链接中的方法在该链接 中我得到了一些解决方法

  1. 使用 SqlAzure 的自动键时使用 GUID 作为键
  2. 如果像我的情况这样的整数键让记录插入并返回并删除它并使用正确的键重新插入它通过关闭身份并设置 identity_insert XXXTable on - 这基本上会关闭 IDENTITY

然后当我用正确的键插入完成时再次打开身份

设置 identity_insert XXXTable 关闭——这基本上打开了 IDENTITY

注意:对于接收大量插入请求的表来说,这不是一个好的解决方案,但对于寻找临时出路的人可能有用

于 2014-05-22T09:52:40.257 回答
0

我也遇到了这个问题,直到这次我找不到任何方法,似乎实体有错误或类似的东西。我在互联网上搜索但一无所获

于 2014-02-25T20:02:29.207 回答
0

似乎没有适用于 SQL Azure 的 TF 272 解决方法。我刚刚注意到 2 个表中的问题(999 和 1000 的差距),并在检查这两个表并检查插入的记录之前认为这是一个安全漏洞。有关详细信息,请参阅此 MS TechNet 讨论的最后一项。有点让人放心,但看起来更像是一个错误而不是一个功能。

于 2013-12-25T17:19:04.833 回答