【问题标题】:Entity Framework 6 DbUpdateConcurrencyException When Adding New Rows To DatabaseEntity Framework 6 DbUpdateConcurrencyException 将新行添加到数据库时
【发布时间】:2016-10-26 01:03:48
【问题描述】:

这是我的问题:每次我尝试向数据库中添加一行时,都会出现以下异常: DbUpdateConcurrencyException

除了我,没有其他人使用这个开发数据库。请帮忙。到这里我就束手无策了。

代码如下:

using (var context = new MyDbContext(connectionString))
{
    context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

    var newProduct = new ProductsEntity { ProductTypeId = 1 };
    context.Products.Add(newProduct);

    // The line below always throws the exception
    context.SaveChanges();
}

上下文,MyDbContext定义如下:

public class MyDbContext : DbContext
{
    public MyDbContext(string connectionString) : base(connectionString)
    {
        System.Data.Entity.Database.SetInitializer<MyDbContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new ProductsMap());
    }

    public virtual DbSet<ProductsEntity> Products { get; set; }
}

ProductsMapProductEntity定义如下:

public class ProductsMap : EntityTypeConfiguration<ProductsEntity>
{
    HasKey(t => t.ProductId);

    ToTable("Products");

    Property(t => t.ProductId)
        .HasColumnName("ProductId")
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    Property(t => t.ProductTypeId)
        .HasColumnName("ProductTypeId");

    // Stored Procedure Mapping
    MapToStoredProcedures(s =>
        s.Insert((i => i.HasName("InsertProduct")
            .Parameter(p => p.ProductTypeId, "ProductTypeId")
            .Result(r => r.ProductId, "ProductId")
            )));
}

public class ProductsEntity
{
    public int ProductId { get; set; }
    public int ProductTypeId { get; set; }
}

存储过程 InsertProduct 在这里:

CREATE PROCEDURE [dbo].[InsertProduct]    

@ProductTypeId int,  
@Identity int = NULL OUTPUT  

AS  
BEGIN    

INSERT INTO [dbo].[Products]  
    ( [ProductTypeId] ) 
    VALUES 
    ( @ProductTypeId  )    

END    

SET @Identity = SCOPE_IDENTITY()  

RETURN   

并且,Products表定义如下:

CREATE TABLE dbo.Products
(
    ProductId int IDENTITY(1,1) NOT NULL,
    ProductTypeId int NOT NULL,
PRIMARY KEY CLUSTERED
(
    ProductId ASC
) WITH (
    PAD_INDEX  = OFF, 
    STATISTICS_NORECOMPUTE  = OFF, 
    IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, 
    ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) 
ON [PRIMARY]

GO

ALTER TABLE dbo.Products WITH CHECK ADD FOREIGN KEY(ProductTypeId)
REFERENCES dbo.ProductTypes (ProductTypeId)

GO

如果有帮助的话,似乎正在发生的事情是 Products 表的身份种子实际上在每次代码点击 SaveChanges() 时都会增加下面的行。我可以判断,因为当我在 SQL Server Studio 中测试运行 InsertProduct 时,ProductId 列在我每次尝试运行此代码时都会跳过一个整数。

我的最佳猜测是 Entity Framework 执行 InsertProduct 过程,但不知何故,事务被阻止并回滚。不过老实说,我不知道。

这是控制台的 Debug 输出内容:

Opened connection at 10/25/2016 5:53:43 PM -07:00

Started transaction at 10/25/2016 5:53:43 PM -07:00

[dbo].[InsertProduct]


-- ProductTypeId: '1' (Type = Int32, IsNullable = false)

-- Executing at 10/25/2016 5:53:44 PM -07:00

Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2016-10-26T00:53:44.0238952Z","tags":{"ai.operation.parentId":"CTWlUsYMEbU=","ai.device.roleInstance":"MyComputerName.Domain","ai.operation.name":"POST deal","ai.operation.id":"CTWlUsYMEbU=","ai.internal.sdkVersion":"rddf: 2.1.0.363"},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"SERVERNAME| DevDatabase | [dbo].[InsertProduct]","id":"JDD838YyJkc=","value":16.0379,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}}
-- Completed in 28 ms with result: SqlDataReader



Closed connection at 10/25/2016 5:53:44 PM -07:00

Exception thrown: 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' in EntityFramework.dll

这是来自内部异常的文本:

“存储更新、插入或删除语句影响了意外数量的行 (0)。自加载实体后,实体可能已被修改或删除。”

而且,确实没有插入任何行。再一次,没有其他人使用这个开发数据库;我是唯一拥有 dbo 权限的人。

编辑:找到解决方案

好的,非常感谢下面的@Juan 为我指明了正确的方向。问题确实是 EF 需要某种方式来了解主键发生了什么,但这非常微妙。

我做的第一件事是尝试像这样更改上面的存储过程:

CREATE PROCEDURE [dbo].[InsertProduct]    

@ProductTypeId int,  
@Identity int = NULL OUTPUT  

AS  
BEGIN    

INSERT INTO [dbo].[Products]  
    ( [ProductTypeId] ) 
    VALUES 
    ( @ProductTypeId  )    

END    

SET @Identity = SCOPE_IDENTITY()  
SELECT SCOPE_IDENTITY() // <-- Added this line

RETURN   

然后,发生的事情是抛出的异常更改为 DbUpdateException(来自 DbUpdateConcurrencyException)。此外,内部异常文本现在如下所示:

函数映射指定结果集不包含的结果列“ProductId”。

嗯,这告诉我我正在取得进步。所以接下来我尝试改变 ProductsMap 中的 Fluent API,如下所示:

public class ProductsMap : EntityTypeConfiguration<ProductsEntity>
{
    HasKey(t => t.ProductId);

    ToTable("Products");

    Property(t => t.ProductId)
        .HasColumnName("ProductId")
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    Property(t => t.ProductTypeId)
        .HasColumnName("ProductTypeId");

    // Stored Procedure Mapping
    // ------------------------
    // Changed the "ProductId" below to "Identity", since that is
    // the name of the output variable in the stored procedure
    MapToStoredProcedures(s =>
        s.Insert((i => i.HasName("InsertProduct")
            .Parameter(p => p.ProductTypeId, "ProductTypeId")
            .Result(r => r.ProductId, "Identity")
            )));
}

上述更改引发了相同的 DbUpdateException,但内部异常文本更改为:

函数映射指定结果集不包含的结果列“Identity”。

此时我注意到 Fluent API 中 .Result() 方法的工具提示如下(重点由我添加):

为此存储过程配置一列结果以映射到属性。 这用于数据库生成的列

所以,这让我想到 Fluent API 中的 .Result() 方法可能根本不适用于 SQL 存储过程输出。我试图查找有关 EF6 的文档以了解该方法应该如何工作,但我找不到任何有用的东西。而且,我找不到任何使用 SQL 存储过程输出的示例。

因此,这让我注意到了我唯一的数据库生成列,即这一列:

    Property(t => t.ProductId)
        .HasColumnName("ProductId")
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

考虑到这一点,我将上面的存储过程更改如下:

CREATE PROCEDURE [dbo].[InsertProduct]    

@ProductTypeId int,  
@Identity int = NULL OUTPUT  

AS  
BEGIN    

INSERT INTO [dbo].[Products]  
    ( [ProductTypeId] ) 
    VALUES 
    ( @ProductTypeId  )    

END    

SET @Identity = SCOPE_IDENTITY()  
// Altered the line below a little bit
SELECT ProductId FROM Products WHERE ProductId = SCOPE_IDENTITY()

RETURN   

再一次,我得到了这个结果 DbUpdateException,:

函数映射指定结果集不包含的结果列“Identity”。

所以,我想,我应该把 Fluent API 中的 .Result() 方法改回我以前的样子,也许一切都会好起来的:

    // Stored Procedure Mapping
    // ------------------------
    // Changed the "Identity" back to "ProductId"
    MapToStoredProcedures(s =>
        s.Insert((i => i.HasName("InsertProduct")
            .Parameter(p => p.ProductTypeId, "ProductTypeId")
            .Result(r => r.ProductId, "ProductId")
            )));

你猜怎么着?成功了!

为了更好地衡量,我还尝试完全删除 .Result() 规范,如下所示:

    // Stored Procedure Mapping
    // ------------------------
    // No more .Result() specification
    MapToStoredProcedures(s =>
        s.Insert((i => i.HasName("InsertProduct")
            .Parameter(p => p.ProductTypeId, "ProductTypeId")
            )));

而且,有趣的是,这也很好用!

因此,最终,我的结论是,将列指定为在 Fluent API 中生成的数据库可能与 MapToStoredProcedures() 下的 .Result() 方法的规范是多余的。我在 EF6 文档中找不到任何东西来更好地解释这一点,但至少现在我可以解决问题了。

再次感谢@Juan 的大力帮助,为我指明了正确的方向! :)

【问题讨论】:

  • 我很抱歉,但是......为什么要麻烦将插入映射到存储过程?为什么不让 EF 做 EF 做的事情呢?话虽如此,通常伴随着这个异常有更多的错误信息。请发帖。
  • 感谢您的评论。 1) 我们不能授予对生产数据库的插入权限。我们只能授予特定存储过程的执行权限,因此需要映射。此外,如果映射到存储过程,通常可以通过多次插入(即 1000 次)获得更好的性能。 EF 写出这么多插入可能会很慢。 2)还有哪些其他错误信息会有所帮助?可能是内部异常?
  • 我可以理解权限。总是很痛苦。有一些方法可以提高 EF 插入的性能。有大量记录的瓶颈通常是由于多次调用 SaveChanges() 或在上下文中积累了未更改的对象。对于批量插入,我也看到了一些不错的扩展。此异常通常会附带一条错误消息。它可能是异常本身的消息,也可能是内部异常中的消息。收集所有可以发布的信息并发布,以便我们为您提供帮助。祝你好运!
  • 谢谢!它不会解决我烦人的权限问题,但我必须研究那些批量插入扩展;这是很好的建议。我同意对 SaveChanges() 的调用应该尽可能少;这也是很好的建议。此外,我在最底部发布了上面内部异常中的文本。希望对您有所帮助!

标签: c# mysql entity-framework-6 ef-fluent-api


【解决方案1】:

我想我可能知道问题出在哪里。

EF 未检测到插入。我认为这是因为您有一个名为 @Identity 的 OUTPUT 参数,但我没有看到它被映射回任何地方:

// Stored Procedure Mapping
MapToStoredProcedures(s =>
    s.Insert((i => i.HasName("InsertProduct")
        .Parameter(p => p.ProductTypeId, "ProductTypeId")
        .Result(r => r.ProductId, "ProductId")
        )));

要么将 @Identity 参数映射到 ProductId 字段,要么让 SP 返回标识以让 EF 知道插入成功:

SELECT SCOPE_IDENTITY();

【讨论】:

  • 这几乎是正确的答案!很接近。我将编辑我上面的帖子以反映有效的方法。再次感谢您的建议!
  • 太棒了。很高兴能帮到你。
猜你喜欢
  • 2014-08-29
  • 1970-01-01
  • 2013-12-16
  • 2012-04-03
  • 1970-01-01
  • 2014-12-04
  • 2019-09-28
  • 2014-08-28
  • 2019-01-11
相关资源
最近更新 更多