Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重新附加分离的实体会引发异常_C#_.net_Entity Framework - Fatal编程技术网

C# 重新附加分离的实体会引发异常

C# 重新附加分离的实体会引发异常,c#,.net,entity-framework,C#,.net,Entity Framework,我正在为我的项目使用EntityFramework6(使用生成的模型和DbContext)。我在程序中使用的体系结构是从应用程序中抽象出数据访问层。这意味着我的实体将大部分处于分离状态 似乎重新连接任何以前连接的实体都是不可能的(或者我做得不好) 我已经单独测试了一个案例,并且能够缩小问题的范围。以下是步骤 从上下文中获取实体并释放上下文。这将分离实体 public async Task<ArchiveEntry> GetEntry(string api, string id) {

我正在为我的项目使用EntityFramework6(使用生成的模型和DbContext)。我在程序中使用的体系结构是从应用程序中抽象出数据访问层。这意味着我的实体将大部分处于分离状态

似乎重新连接任何以前连接的实体都是不可能的(或者我做得不好)

我已经单独测试了一个案例,并且能够缩小问题的范围。以下是步骤

  • 从上下文中获取实体并释放上下文。这将分离实体

    public async Task<ArchiveEntry> GetEntry(string api, string id)
    {
        // Omitted some code for simplicity
        ArchiveEntry entry;
    
        using (var db = new TwinTailDb())
        {
            entry = await db.ArchiveEntries.Where(a => a.Id == id).SingleOrDefaultAsync();
        }
    
        return entry;
    }
    
  • 最后,重新附着分离的实体

    public async Task RevertChanges()
    {
        using (var db = new TwinTailDb())
        {
            if (db.Entry(this).State == EntityState.Detached && Id != 0)
            {
                db.ArchiveEntries.Attach(this);
                //await db.Entry(this).ReloadAsync(); // Commented since this is irrelevant
            }
        }
    }
    
  • 然后我运行这个函数来使用上面的代码

    public async Task Test()
    {
        ArchiveEntry entry = await ArchiveService.GetEntry(null, "7");
        await entry.Save();
        await entry.RevertChanges();
    }
    
  • 然后抛出以下错误:

    附加“TwinTail.Entities.ArchiveEntry”类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体状态跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”

    这里有一个重要的观点。如果跳过步骤2,它不会抛出异常

    我的推测是,实体是在不同的上下文中修改和保存的。如果跳过步骤2,实体将保持不变,因此重新附着它不会造成问题(只是猜测)。但是,该实体已经处于分离状态,因此这应该是无关的

    另一点是,
    ChangeTracker
    在这些步骤中不包含任何内容。此外,如果我对分离的实体执行任何上下文操作,它会抛出一个异常,表示应该首先附加它。我还注意到内部_entitywrapper仍然有一个对旧上下文的引用

    最后,问题来了。如何正确地重新附加实体,以及为什么会发生此异常

    我在另一个问题()中问了一些类似的问题,但我觉得我需要发布一个新的问题,因为这是一个更一般的问题

    我在程序中使用的体系结构是从应用程序中抽象出数据访问层

    看起来您正在
    ArchiveEntry
    类本身上实现这些方法。这不是一个抽象的数据访问层,将实体传递给许多像这样的短期上下文会给您带来麻烦

    您不应该给实体类自己的方法来管理持久性问题,而应该将代码放在与实体分开的类中,并确保一旦实体连接到上下文,您就可以继续使用相同的上下文,直到它被释放。一旦上下文被释放,如果您想对实体执行另一个操作,您应该在尝试使用它执行任何(附加的)操作之前从新上下文中检索它

    我在程序中使用的体系结构是从应用程序中抽象出数据访问层

    看起来您正在
    ArchiveEntry
    类本身上实现这些方法。这不是一个抽象的数据访问层,将实体传递给许多像这样的短期上下文会给您带来麻烦

    您不应该给实体类自己的方法来管理持久性问题,而应该将代码放在与实体分开的类中,并确保一旦实体连接到上下文,您就可以继续使用相同的上下文,直到它被释放。一旦上下文被释放,如果您想对实体执行另一个操作,您应该在尝试对其执行任何(附加)操作之前从新上下文中检索它。

    我使用的是AsNoTracking(),因此,这个异常对我来说很奇怪。下面的代码修复了我代码中的问题。我想我可以在try块中使用代码,并且只使用catch块,但是我从未测试过这个概念

    具有PrimaryKey的实体基类

    public class EntityBase<K>
    {
        [NotMapped]
        public virtual K PrimaryKey {get;}
    }
    
    公共类EntityBase
    {
    [未映射]
    公共虚拟K主密钥{get;}
    }
    
    更新功能

    public static void Update<T,K>(DbContext context, T t) where T:EntityBase<K>
    {
        DbSet<T> set = context.Set<T>();
        if(set==null) return;
        if(context.Entry<T>(t).State == EntityState.Detached)
        {
             try
             {
                 T attached = set.Attached(t);
                 context.Entry<T>(attached).State = EntityState.Modified;
             }catch(Exception ex)
             {
                 T found = set.Find(t.PrimaryKey);
                 context.Entry(found).CurrentValues.SetValues(t);
             }
         }
         context.SaveChanges();
    }
    
    publicstaticvoidupdate(DbContext-context,T),其中T:EntityBase
    {
    DbSet set=context.set();
    if(set==null)返回;
    if(context.Entry(t.State==EntityState.Distached)
    {
    尝试
    {
    T附加=设置附加(T);
    context.Entry(附件).State=EntityState.Modified;
    }捕获(例外情况除外)
    {
    T found=set.Find(T.PrimaryKey);
    context.Entry(find).CurrentValues.SetValues(t);
    }
    }
    SaveChanges();
    }
    
    扩展函数

    public static void Update<T,K>(this DbContext context, T t) where T:EntityBase<K> => Update<T,K>(context, t);
    
    publicstaticvoidupdate(这个DbContext上下文,T),其中T:EntityBase=>Update(context,T);
    
    我使用的是AsNoTracking(),所以这个异常对我来说很奇怪。下面的代码修复了我代码中的问题。我想我可以在try块中使用代码,并且只使用catch块,但是我从未测试过这个概念

    具有PrimaryKey的实体基类

    public class EntityBase<K>
    {
        [NotMapped]
        public virtual K PrimaryKey {get;}
    }
    
    公共类EntityBase
    {
    [未映射]
    公共虚拟K主密钥{get;}
    }
    
    更新功能

    public static void Update<T,K>(DbContext context, T t) where T:EntityBase<K>
    {
        DbSet<T> set = context.Set<T>();
        if(set==null) return;
        if(context.Entry<T>(t).State == EntityState.Detached)
        {
             try
             {
                 T attached = set.Attached(t);
                 context.Entry<T>(attached).State = EntityState.Modified;
             }catch(Exception ex)
             {
                 T found = set.Find(t.PrimaryKey);
                 context.Entry(found).CurrentValues.SetValues(t);
             }
         }
         context.SaveChanges();
    }
    
    publicstaticvoidupdate(DbContext-context,T),其中T:EntityBase
    {
    DbSet set=context.set();
    if(set==null)返回;
    if(context.Entry(t.State==EntityState.Distached)
    {
    尝试
    {
    T附加=设置附加(T);
    context.Entry(附件).State=EntityState.Modified;
    }捕获(例外情况除外)
    {
    T found=set.Find(T.PrimaryKey);
    context.Entry(find).CurrentValues.SetValues(t);
    }
    }
    SaveChanges();
    }
    
    扩展函数

    public static void Update<T,K>(this DbContext context, T t) where T:EntityBase<K> => Update<T,K>(context, t);
    
    公共静态无效更新(th