2016-02-02 21 views
0

我在使用实体框架6.实体框架与过滤指数 - “无法插入重复键行对象”

CREATE TABLE [dbo].[ItemImage](
    [ItemId] [int] NOT NULL, 
    [stream_id] [uniqueidentifier] ROWGUIDCOL NOT NULL, 
    [Primary] [bit] NOT NULL, 
    [Caption] [nvarchar](1000) NULL, 
    CONSTRAINT [PK_ItemImage_ItemId_stream_id] PRIMARY KEY CLUSTERED ([ItemId] ASC, [stream_id] ASC) 
    CONSTRAINT [FK_ItemImage_ItemId_Item] FOREIGN KEY([ItemId]) REFERENCES [dbo].[Item] ([ItemId]) 
); 
GO 

CREATE UNIQUE NONCLUSTERED INDEX [UXF_ItemImage_ItemId_Primary] 
    ON [dbo].[ItemImage] ([ItemId] ASC) 
    WHERE ([Primary] = 1); 
GO 
背后的MVC的Web应用程序位于数据库以下简化表定义和过滤唯一索引

唯一索引可防止设置[Primary]位标志的多个[ItemId]

在MVC控制器,我有ItemImage,我有如下更新EF模型视图模型的集合:

... 
foreach (var img in itemViewModel.ItemImages) 
{ 
    var itemImage = item.ItemImages.First(i => i.stream_id == img.stream_id); 

    itemImage.Primary = img.Primary; 
    itemImage.Caption = img.Caption; 
} 
... 
await db.SaveChangesAsync(); 

db.SaveChangesAsync()叫我出现以下情况例外:

无法在具有唯一索引'UXF_ItemImage_ItemId_Primary'的对象'dbo.ItemImage'中插入重复键行。重复的键值是(146)。 该声明已被终止。

我有逻辑,其防止一个具有多个Item“初级” ItemImages的更新之前。

我认为这是因为当实体框架试图更新数据库中的ItemImages集合时,它将另一行的[Primary]标志设置为1,然后取消设置当前设置的行。

有没有办法强制在实体框架更新的顺序?或者是否有我可以实施的解决方法?

回答

0

不知道这是否是最有效的方法,但现在正在使用以下方法。

foreach (var img in itemViewModel.ItemImages.Where(i => ! i.Primary)) 
{ 
    var itemImage = item.ItemImages.First(i => i.stream_id == img.stream_id); 
    itemImage.Primary = img.Primary; 
    itemImage.Caption = img.Caption; 
} 

await db.SaveChangesAsync(); 

var primaryImage = itemViewModel.ItemImages.First(i => i.Primary); 
var itemImagePrimary = item.ItemImages.First(i => i.stream_id == primaryImage.stream_id); 
itemImagePrimary.Primary = primaryImage.Primary; 
itemImagePrimary.Caption = primaryImage.Caption; 

await db.SaveChangesAsync(); 

基本上我刚刚更新所有的“非主” ItemImages批量首先,提交更改,然后更新新的“第一”。

+1

这个貌似正确的做法,据我所知,你无法控制的顺序命令。如果出现其他问题,我会将这一切全部包装在一个事务中(使用'db.Database.BeginTransaction()')来防止一半的更新 – Jcl

+0

谢谢,这是一个很好的观点! –

0

更有效的方法是通过存储过程将更新推送到数据库层。

CREATE PROCEDURE [dbo].[uspItemImage_SetPrimary] 
    @itemId INT 
, @stream_id UNIQUEIDENTIFIER 
AS 
BEGIN 

    SET NOCOUNT ON; 

    IF NOT EXISTS (SELECT NULL FROM [dbo].[ItemImage] WHERE [ItemId] = @itemId AND [stream_id] = @stream_id) 
    BEGIN 
     DECLARE @stream_id_vc NVARCHAR(36) = CONVERT(NVARCHAR(36), NEWID()); 
     RAISERROR( N'No ItemImage exists with ItemId = %d and stream_id = ''%s''', 
       11, 
       1, 
       @itemId, 
       @stream_id_vc); 
    END; 

    BEGIN TRANSACTION; 

    UPDATE 
     [dbo].[ItemImage] 
    SET 
     [Primary] = 0 
    WHERE 
     [ItemId] = @itemId; 

    UPDATE 
     [dbo].[ItemImage] 
    SET 
     [Primary] = 1 
    WHERE 
     [ItemId] = @itemId AND 
     [stream_id] = @stream_id; 

    COMMIT TRANSACTION; 

    RETURN 0; 

END; 

导入存储过程到实体框架模型作为SetPrimaryItemImage后,我可以简单地做到以下几点:

... 
foreach (var img in itemViewModel.Images) 
{ 
    var itemImage = item.ItemImages.First(i => i.stream_id == img.stream_id); 
    itemImage.Caption = img.Caption; 
} 
await db.SaveChangesAsync(); 

db.SetPrimaryItemImage(item.ItemId, itemViewModel.Images.First(i => i.Primary).stream_id); 
...