简体   繁体   English

使用复合键更新记录 - Entity Framework Core

[英]Update Record with Composite Key - Entity Framework Core

I am having an issue updating a record with a composite key using EF Core 5 and Net Core 5. I am able to get the record using both parts of the composite key (two ids in my case) but when I save the changes to the database I get the following error:我在使用 EF Core 5 和 Net Core 5 使用复合键更新记录时遇到问题。我可以使用复合键的两个部分(在我的例子中是两个 id)来获取记录,但是当我将更改保存到数据库我收到以下错误:

Database operation expected to affect 1 row(s) but actually affected 24 row(s).数据库操作预计会影响 1 行,但实际上影响了 24 行。 Data may have been modified or deleted since entities were loaded.自加载实体以来,数据可能已被修改或删除。 See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=527962

After looking in the database, it looks like entity framework is trying to update all the records in the table with same id (first part of the composite key) instead of updating the record that I got.查看数据库后,实体框架似乎正在尝试更新表中具有相同 ID(复合键的第一部分)的所有记录,而不是更新我获得的记录。

Has anyone else seen this behavior?还有其他人看到过这种行为吗? Any ideas of how to get around it or what I might be doing wrong?关于如何解决它或我可能做错了什么的任何想法?

Here is a basic version of the table set up这是表格设置的基本版本

Table1:表格1:

Id (PK)

Table2:表二:

Id (PK),  
Table1Id (PK, FK)

Scenario where it fails:失败的场景:

Table1:表格1:

Id  
--
1  
2  

Table2:表二:

Id, Table1Id  
------------
1, 1  
2, 1  
1, 2  
2, 2  

If I try to update the first record in Table2 (1, 1), EF would try to update it and the third record (1, 2) and throw the above error.如果我尝试更新 Table2 (1, 1) 中的第一条记录,EF 将尝试更新它和第三条记录 (1, 2) 并抛出上述错误。

Code:代码:

// Getting record    
var record = await context.Table2s
                          .FirstOrDefaultAsync(e => e.Id == 1 && e.Table1Id == 1, cancellationToken);

// Saving
await context.SaveChangesAsync(cancellationToken);

// I have also tried specifying which record to update before saving
context.Table2s.Update(record);

Configuration for Table2:表 2 的配置:

builder.ToTable("Table2");
builder.HasKey(e => new { e.Id, e.Table1Id });

builder.HasOne(e => e.Table1)
                .WithMany(m => m.Table2s)
                .HasForeignKey(e => e.Table1Id)
                .OnDelete(DeleteBehavior.Cascade);

It works in my own projects, I hope it will be useful.它适用于我自己的项目,我希望它会有用。

protected override void OnModelCreating(DbModelBuilder mb) {
   mb.Entity<WebServisUser>().HasKey(x => new
   {
       x.Proje, /* Composite Keys */
       x.KulAdi /* Composite Keys */
   });
   mb.Entity<WebServisUser>().Property(e => e.Proje).IsUnicode(false);
   mb.Entity<WebServisUser>().Property(e => e.KulAdi).IsUnicode(false);
}

I have added the link other functions for better understanding.为了更好地理解,我添加了其他功能的链接。

public static class Helper
    {
        public static bool IsNull(this object value) => (value == null || Convert.IsDBNull(value) || Convert.GetTypeCode(value) == TypeCode.Empty);
        public static bool IsNotNull(this object value) => !value.IsNull();
        public static string ToPropertyName(this Expression value)
        {
            if (typeof(LambdaExpression).IsAssignableFrom(value.GetType())) { value = ((LambdaExpression)value).Body; }
            if (value is MemberExpression e_mem) { return e_mem.Member.Name; }
            else if (value is UnaryExpression e_una) { return ((MemberExpression)e_una.Operand).Member.Name; }
            else { throw new InvalidCastException(String.Format("value type can be \"{0}\" or \"{1}\"", nameof(MemberExpression), nameof(UnaryExpression))); }
        }
        public static object ChangeType(object value, Type type)
        {
            var c = TryTypeIsNullable(type, out Type _outtype);
            return (c && value.IsNull()) ? null : (_outtype.IsEnum ? Enum.ToObject(_outtype, value) : Convert.ChangeType(value, c ? Nullable.GetUnderlyingType(type) : type));
        }
        public static void SetPropertyValue<T>(this T value, string propertyname, object data) where T : class
        {
            var p = typeof(T).GetProperty(propertyname);
            p.SetValue(value, data.IsNull() ? null : ChangeType(data, p.PropertyType));
        }
        public static bool TryTypeIsNullable(Type value, out Type outvalue)
        {
            var t = (value.IsGenericType && value.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
            outvalue = (t ? value.GenericTypeArguments[0] : value); return t;
        }
        public static bool TryTypeIsAttribute<T, Y>(T value, out Y outvalue) where T : ICustomAttributeProvider where Y : Attribute
        {
            bool ret;
            try
            {
                var v = value.GetType();
                var y = typeof(Y);
                if (typeof(Assembly).IsAssignableFrom(v) && Attribute.IsDefined((Assembly)((object)value), y)) { ret = true; }
                else if (typeof(MemberInfo).IsAssignableFrom(v) && Attribute.IsDefined((MemberInfo)((object)value), y)) { ret = true; }
                else if (typeof(Module).IsAssignableFrom(v) && Attribute.IsDefined((Module)((object)value), y)) { ret = true; }
                else if (typeof(ParameterInfo).IsAssignableFrom(v) && Attribute.IsDefined((ParameterInfo)((object)value), y)) { ret = true; }
                else { ret = false; }
                outvalue = ret ? value.GetCustomAttributes(y, false).Cast<Y>().Take(1).FirstOrDefault() : default;
            }
            catch
            {
                outvalue = default;
                ret = false;
            }
            return ret;
        }
        public static void SetCompositeKey<T, TKey>(this DbContext context, T entity, Expression<Func<T, TKey>> compositekey, TKey compositekeyvalue, Action<T> actionislem = null) where T : class, new()
        {
            var t = typeof(T);
            var c = compositekey.ToPropertyName();
            var ie_prop = typeof(T).GetProperties().Where(x => x.GetMethod.IsNotNull() && x.SetMethod.IsNotNull() && !x.GetMethod.IsVirtual && !x.SetMethod.IsVirtual && !Helper.TryTypeIsAttribute(x, out NotMappedAttribute _)).Select(x => new
            {
                name = x.Name,
                iskey = x.Name == c,
                iscompositekey = Helper.TryTypeIsAttribute(x, out KeyAttribute _outkeyvalue) && _outkeyvalue.IsNotNull() && Helper.TryTypeIsAttribute(x, out DatabaseGeneratedAttribute _outdatagenvalue) && _outdatagenvalue.DatabaseGeneratedOption == DatabaseGeneratedOption.None
            });
            if (ie_prop.Where(x => x.iscompositekey).Count() < 2) { throw new KeyNotFoundException("minimum of 2 composite keys exception message..."); }
            else if (ie_prop.Any(x => x.iscompositekey && x.iskey))
            {
                var addentity = new T();
                var e = context.Entry<T>(entity);
                var dbset = context.Set<T>();
                dbset.Attach(entity);
                foreach (var item in ie_prop.Select(x => new
                {
                    x.name,
                    x.iskey
                })) { addentity.SetPropertyValue(item.name, item.iskey ? compositekeyvalue : e.Property(item.name).OriginalValue); }
                dbset.Add(addentity); /* Insert record about new key value */
                if (actionislem.IsNotNull()) { actionislem(addentity); } /* If there are other areas of connection, do the operations there with the action method. */
                dbset.Remove(entity); /* delete old value */
            }
            else { throw new Exception("compositekey must be composite key exception message..."); }
        }
    }

and example和例子

using (var mdl = new mdlBayUniOrtak())
            {
                using (var c = new TransactionScope())
                {
                    var beforeentity = mdl.WebServisUsers.Where(x => x.Proje == "loremipsum").Take(1).FirstOrDefault();
                    var e = new Action<WebServisUser>((WebServisUser afterentity) =>
                    {
                        /* Other actions if necessary. Other foreign key operations can be done here! */
                    });
                    mdl.SetCompositeKey(beforeentity, x => x.KulAdi, "galatasaray", e);
                    mdl.SaveChanges();
                    c.Complete();
                }
            }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM