.net 我是否可以将单个实体附加到多个上下文

.net 我是否可以将单个实体附加到多个上下文,.net,entity-framework,.net,Entity Framework,在我自定义开发的应用程序中,我遇到了一个错误“entity对象不能被多个IEntityChangeTracker实例引用”。这意味着,据我所知,我不能使用附加到多个上下文的同一实体。为了重现错误,我使用以下代码创建了一个沙盒应用程序: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; using

在我自定义开发的应用程序中,我遇到了一个错误“entity对象不能被多个IEntityChangeTracker实例引用”。这意味着,据我所知,我不能使用附加到多个上下文的同一实体。为了重现错误,我使用以下代码创建了一个沙盒应用程序:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Linq.Expressions;

namespace EntityFrameworkRepositoryPatternTestApp
{
  public class Item
  {
    public Guid Id { get; set; }
    public string SomeString { get; set; }
    public int SomeInt { get; set; }
    public double SomeDouble { get; set; }
  }

  public class Context : DbContext
  {
    public DbSet<Item> Items { get; set; }

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

    public Context() : base("Connection")
    {
    }
  }

  public class ItemConfiguration : EntityTypeConfiguration<Item>
  {
    public ItemConfiguration()
    {
      this.ToTable("Items").HasKey(i => i.Id);
      this.Property(i => i.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
      this.Property(i => i.SomeString).HasMaxLength(255);
      this.Property(i => i.SomeInt);
      this.Property(i => i.SomeDouble);
    }
  }

  public interface IRepository<T>
  {
    T Add(T item);
    IEnumerable<T> Get(Expression<Func<T, bool>> filter = null);
    T GetById(Guid id);
    void Remove(T item);
  }

  public class EntityFrameworkRepository<TEntity> : IRepository<TEntity> where TEntity : class
  {
    private readonly IDbSet<TEntity> dbSet;
    private Context Context { get; }

    public TEntity Add(TEntity item)
    {
      this.dbSet.Add(item);
      return item;
    }

    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null)
    {
      IQueryable<TEntity> query = this.dbSet;
      if (filter != null)
        query = query.Where(filter);
      return query.ToList();
    }

    public TEntity GetById(Guid id)
    {
      return this.dbSet.Find(id);
    }

    public void Remove(TEntity item)
    {
      this.dbSet.Remove(item);
    }

    public EntityFrameworkRepository(Context context)
    {
      this.Context = context;
      this.dbSet = this.Context.Set<TEntity>();
    }
  }

  public interface IDomainContext
  {
    int SaveChanges();
    int SaveChangesWithoutClientValidation();
  }

  public interface IDomainContext<TEntity> : IDomainContext where TEntity : class
  {
    IRepository<TEntity> Entities { get; }

    void Update(TEntity item);
    void Attach(TEntity item);
    void Detach(TEntity item);
  }

  public class DomainContext<TEntity> : IDomainContext<TEntity>, IDisposable where TEntity : class
  {
    private Context context;

    public int SaveChanges()
    {
      return this.context.SaveChanges();
    }

    public IRepository<TEntity> Entities => this.GetRepository<TEntity>();

    public void Update(TEntity item)
    {
      var entry = this.context.Entry(item);
      foreach (var propertyName in entry.OriginalValues.PropertyNames)
      {
        var original = entry.OriginalValues.GetValue<object>(propertyName);
        var current = entry.CurrentValues.GetValue<object>(propertyName);
        if ((original == null && current != null) || 
            ((original != null) && !original.Equals(current)))
          entry.Property(propertyName).IsModified = true;
      }
    }

    public void Attach(TEntity item) =>
      this.context.Set<TEntity>().Attach(item);

    public void Detach(TEntity entity) =>
      ((IObjectContextAdapter)this.context).ObjectContext.Detach(entity);

    public void Dispose()
    {
      this.context.Dispose();
    }

    private IRepository<T> GetRepository<T>() where T : class
    {
      var resultRepository = new EntityFrameworkRepository<T>(this.context);
      return resultRepository;
    }

    public DomainContext()
    {
      this.context = new Context();
    }
  }

  public class Program
  {
    static void Main(string[] args)
    {
      var domainContext = new DomainContext<Item>();
      {
        var item = new Item() { Id = Guid.NewGuid(), SomeDouble = 666, SomeInt = 777, SomeString = "some string"};
        domainContext.Entities.Add(item);
        domainContext.SaveChanges();
        using (var domainContext2 = new DomainContext<Item>())
        {
          if (item != null)
          {
            domainContext2.Attach(item);
            item.SomeDouble = 11.28;
            domainContext2.Update(item);
            domainContext2.SaveChanges();
          }
        }
      }
      domainContext.Dispose();
    }
  }
}
使用系统;
使用System.Collections.Generic;
使用System.ComponentModel.DataAnnotations.Schema;
使用System.Data.Entity;
使用System.Data.Entity.Infrastructure;
使用System.Data.Entity.ModelConfiguration;
使用System.Linq;
使用System.Linq.Expressions;
命名空间EntityFrameworkRepositoryPatternTestApp
{
公共类项目
{
公共Guid Id{get;set;}
公共字符串SomeString{get;set;}
公共int SomeInt{get;set;}
公共双精度SomeDouble{get;set;}
}
公共类上下文:DbContext
{
公共数据库集项{get;set;}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(newitemconfiguration());
基于模型创建(modelBuilder);
}
公共上下文():基(“连接”)
{
}
}
公共类ItemConfiguration:EntityTypeConfiguration
{
公共项配置()
{
this.ToTable(“Items”).HasKey(i=>i.Id);
this.Property(i=>i.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(i=>i.SomeString).HasMaxLength(255);
this.Property(i=>i.SomeInt);
this.Property(i=>i.SomeDouble);
}
}
公共接口假定
{
T增加(T项);
IEnumerable Get(表达式过滤器=null);
T GetById(Guid id);
消除无效(T项);
}
公共类EntityFrameworkRepository:IRepository,其中tenty:class
{
专用只读IDbSet数据库集;
私有上下文{get;}
公共帐篷添加(帐篷项目)
{
本.dbSet.Add(项目);
退货项目;
}
公共IEnumerable Get(表达式筛选器=null)
{
IQueryable query=this.dbSet;
if(过滤器!=null)
query=query.Where(过滤器);
返回query.ToList();
}
公共权限GetById(Guid id)
{
返回此.dbSet.Find(id);
}
公共无效删除(临时项目)
{
此.dbSet.Remove(项目);
}
公共EntityFrameworkRepository(上下文)
{
this.Context=Context;
this.dbSet=this.Context.Set();
}
}
公共接口IDomainContext
{
int SaveChanges();
int SaveChangesWithoutClientValidation();
}
公共接口IDomainContext:IDomainContext其中tenty:class
{
i假定实体{get;}
无效更新(潜在项目);
无效附加(TEntity项);
无效分离(张力项);
}
公共类DomainContext:IDomainContext,IDisposable其中tenty:class
{
私人语境;
公共int SaveChanges()
{
返回此.context.SaveChanges();
}
public IRepository Entities=>this.GetRepository();
公共无效更新(临时项目)
{
var entry=this.context.entry(项目);
foreach(条目中的var propertyName.OriginalValues.PropertyNames)
{
var original=entry.OriginalValues.GetValue(propertyName);
var current=entry.CurrentValues.GetValue(propertyName);
如果((原始==null&¤t!=null)|
((original!=null)和&!original.Equals(current)))
entry.Property(propertyName).IsModified=true;
}
}
公共无效附加(临时项目)=>
this.context.Set().Attach(项);
公共无效分离(TEntity实体)=>
((IObjectContextAdapter)this.context).ObjectContext.Detach(实体);
公共空间处置()
{
this.context.Dispose();
}
私有IRepository GetRepository(),其中T:class
{
var resultRepository=newentityframeworkrepository(this.context);
返回结果阳性;
}
公共域上下文()
{
this.context=新上下文();
}
}
公共课程
{
静态void Main(字符串[]参数)
{
var domainContext=new domainContext();
{
var item=new item(){Id=Guid.NewGuid(),SomeDouble=666,SomeInt=777,SomeString=“some string”};
domainContext.Entities.Add(项);
domainContext.SaveChanges();
使用(var domainContext2=new DomainContext())
{
如果(项!=null)
{
domainContext2.附加(项目);
第1.2项=11.28;
domainContext2.更新(项目);
domainContext2.SaveChanges();
}
}
}
domainContext.Dispose();
}
}
}
问题是,我在这个沙盒应用程序中实际上没有得到错误。所以现在我需要了解,实体框架是否真的禁止将单个实体附加到多个上下文,以及如何克服这个限制。我考虑的解决方案是在将实体副本附加到另一个上下文时创建实体副本。但现在我认为,在不创建副本的情况下,将实体附加到另一个上下文确实是可能的

只能附加到多个上下文,因为它们引用了创建它们的上下文

声明

var item = new Item() ...
…创建一个简单的POCO,它对上下文一无所知,即使它连接到上下文也不知道

如果你用…代替这个

var item = context.Items.Create();
item
将是一个代理对象,您会注意到,尝试将其附加到另一个上下文将抛出“无法被多个实例引用”异常


此外,当上下文从数据库中读取对象时,它将是一个代理对象(前提是实体类成为代理的所有条件都满足)。

我认为您缺少的关键是,一个实体在给定时间只能由一个上下文跟踪。您可以将实体的状态设置为
EntityState.Detached
以停止对其进行跟踪,然后将其附加到