AddOrUpdate 违反唯一索引

Posted

技术标签:

【中文标题】AddOrUpdate 违反唯一索引【英文标题】:AddOrUpdate violates unique index 【发布时间】:2017-03-30 17:41:01 【问题描述】:

我正在 EF 的帮助下在 ASP.NET 中编写一个 MVC 应用程序,并且我正在尝试为我的数据库播种。我有以下型号:

public class Team

    [ScaffoldColumn(false)]
    public int Id  get; set; 

    [ForeignKey("ParentTeam")]
    public int? ParentTeamId  get; set; 

    [Required(ErrorMessage = "Cannot create a Team without a name")]
    [Index(IsUnique = true)]
    [MaxLength(30)]
    public string Name  get; set; 

    public IEnumerable<string> Members  get; set; 

    public virtual Team ParentTeam  get; set; 

    public Team()  

    public Team(string name)
    
        Name = name;
    

我的迁移说:

var team = new Team("Admin");
var team2 = new Team("Test Team");
var team3 = new Team("Test Team 2");
context.Teams.AddOrUpdate(t => t.Name, team, team2, team3);
context.SaveChanges();

然后,当我运行 Update-Database 时,我得到:

System.Data.SqlClient.SqlException:无法在其中插入重复的键行 具有唯一索引“IX_Name”的对象“dbo.Teams”。重复键 值为(管理员)。

这有点令人困惑——我以为我告诉AddOrUpdate 用它们的名称来识别要更新的行,但这并没有发生。我无法将Name 添加到Team 的主键中,因为它有一个自引用外键(我可以将ParentTeamName 添加为属性,但我认为没有必要这样做)。我是否误解了AddOrUpdate 的行为?我是不是指定的条件有误?

【问题讨论】:

我不能重复使用 EF 6.2.0 的问题 表格在种子中正确填充您的代码。 【参考方案1】:

我也有同样的原因。在我的情况下,它工作正常,直到我需要使用唯一索引,当它坏了。

我的解决方案是创建一个CustomAddOrUpdate 方法,我首先尝试根据Where predicate 查找现有实例。如果我找到它,我只是更新属性,如果没有,它会被添加到上下文中。

但是,在更新实例之前,我必须将键值从原始实例复制到新实例,以避免 EF 异常告诉您无法更改键属性。

这里是sn-ps的代码:

1) 首先是上下文类中的代码

public void CustomAddOrUpdate<TEntity>(Expression<Func<TEntity, bool>> whereExpression, TEntity entity) where TEntity : class

    var entitySet = this.EntitySet<TEntity>();
    var foundInstance = entitySet.Where(whereExpression).FirstOrDefault();
    if (foundInstance != null)
    
        CopyKeyProperties<TEntity>(foundInstance, entity);
        Entry(foundInstance).CurrentValues.SetValues(entity);
    
    else
    
        entitySet.Add(entity);
    


private void CopyKeyProperties<TEntity>(TEntity source, TEntity target) where TEntity : class

    string[] keys = this.GetKeyNames<TEntity>();
    foreach(var keyName in keys)
    
        Entry(target).Property(keyName).CurrentValue = Entry(source).Property(keyName).CurrentValue;
    

2) 然后在我的种子代码上:

    var entityList = new List<MyExempleEntity>() 
     
        new MyExampleEntity  Prop1 = "a p1", Prop2 = "a p2" ,
        new MyExampleEntity  Prop1 = "b p1", Prop2 = "b p2" ,
        new MyExampleEntity  Prop1 = "c p1", Prop2 = "c p2" ,
    
    foreach(var item in entityList)
    
        context.CustomAddOrUpdate<MyExampleEntity>(x => x.Prop1 == item.Prop1 && x.Prop2 == item.Prop2, item);
    
    context.SaveChanges()

3) 总结一下,这里是从实体获取 KeyProperties 的代码:

using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace System.Data.Entity

    public static class DbContextExtensions
    
        public static string[] GetKeyNames<TEntity>(this DbContext context)
            where TEntity : class
        
            return context.GetKeyNames(typeof(TEntity));
        

        public static string[] GetKeyNames(this DbContext context, Type entityType)
        
            if (context == null)
            
                throw new ArgumentNullException(nameof(context));
            

            MetadataWorkspace metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

            // Get the mapping between CLR types and metadata OSpace
            var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

            // Get metadata for given CLR type
            var entityMetadata = metadata
                    .GetItems<EntityType>(DataSpace.OSpace)
                    .Single(e => objectItemCollection.GetClrType(e) == entityType);

            return entityMetadata.KeyProperties.Select(p => p.Name).ToArray();
        
    

以上代码摘自此博客: https://romiller.com/2014/10/07/ef6-1-getting-key-properties-for-an-entity/

【讨论】:

以上是关于AddOrUpdate 违反唯一索引的主要内容,如果未能解决你的问题,请参考以下文章

mysql插入记录时违反唯一索引的处理

mysql插入记录时违反唯一索引的处理

mysql插入记录时违反唯一索引的处理

mongodb 索引唯一性约束

唯一性约束和唯一性索引的区别

MongoDb在并非所有文档中的字段上创建唯一索引[重复]