如何将实体从一个Entity Framework上下文复制到另一个实体框架上下文?

时间:2014-10-27 19:31:39

标签: c# .net entity-framework entity-framework-6

如何将实体从一个上下文(从DbContext继承)复制到另一个上下文?

我发现的所有内容仅适用于ObjectContext但不适用于DbContext或使用DbContext但不起作用。

例如,我找到了/尝试过:

  • 在CodeProject上使用ObjectContext:CloneHelper
  • 将LazyLoadingEnabled设置为false会导致未填充ICollection<>属性(外键)
  • 将ProxyCreationEnabled设置为false会导致保持ICollection<> property as null(foreign-keys)
  • 条目<> .State =分离导致未填写ICollection<>如果在附加属性之前设置了属性(外键),或者如果稍后设置则阻止清除Id。
  • AsNoTracking()导致异常(以下之一,取决于是否首先从父级的ICollection<>属性或父级首先插入远程上下文中的项目):
    • 父级:“ModelFirstSub_First_Target”角色中的对象无法自动添加到上下文中,因为它是使用NoTracking合并选项检索的。在定义关系之前,将实体显式附加到ObjectContext。
    • 项目:无法添加或附加对象,因为其EntityReference的EntityKey属性值与此对象的EntityKey不匹配。

我将它用于两个目的:

  1. 将所有内容从一个数据库复制到另一个数据库(目标将是原始数据库的克隆;远程数据库的本地副本)。应该保留对象的数量。
  2. 将所选实体从一个数据库添加或更新到另一个数据库(本地缓存中的上游更改为远程源)。不需要保留新创建的对象的ID。
  3. 如何执行该操作?

    EF 6.1.1,.NET 4.5.2,C#

    以下是尝试模拟第二个操作的测试代码(上游更改回远程数据库):

    var addedFirst = localContext.Firsts.AsNoTracking().Single(m => m.Id == 4);
    var updatedFirst = localContext.Firsts.AsNoTracking().Single(m => m.Id == 2);
    
    addedFirst.Items.First().Id = 0;
    addedFirst.Items.First().FirstId = 0;
    addedFirst.Id = 0;
    
    remoteContext.FirstItems.Add(addedFirst.Items.First());
    remoteContext.Firsts.Add(addedFirst);
    
    var originalFirst = remoteContext.Firsts.Single(m => m.Id == 2);
    var originalItem = originalFirst.Items.First();
    originalItem.Title = updatedFirst.Items.First().Title;
    

    以下是模型:

    public class ModelFirstBase
    {
        public virtual int Id { get; set; }
        public virtual ICollection<ModelFirstSub> Items { get; set; }
        public virtual string Title { get; set; }
    }
    
    public class ModelFirstSub
    {
        public virtual int Id { get; set; }
        public virtual int FirstId { get; set; }
        public virtual ModelFirstBase First { get; set; }
        public virtual string Title { get; set; }
    }
    

    PS:当模型与生产应用程序共享时,属性需要保持虚拟状态,这将受益于动态代理。

2 个答案:

答案 0 :(得分:0)

您是否尝试将模型简化为第二个模型:

public class ModelFirstBase
{
public virtual int Id { get; set; }
public virtual ICollection<ModelFirstSub> Items { get; set; }
public virtual string Title { get; set; }
}

public class ModelFirstSub
{
public virtual int Id { get; set; }
public virtual int FirstId { get; set; }
public virtual ModelFirstBase First { get; set; }
public virtual string Title { get; set; }
}

public class ModelTranslator
{
public ModelFirstSub TranslateModelFirstBase(ModelFirstBase entity)
{
//do some error handling and null checks.
var second = new ModelFirstSub(){
                  FirstId = entity.Id,
                  .....
             };
return second;
}
}

public void TransferModels(){ //I haven't thought about disposing stuff, you should.
var firstContext = new FirstContext();
var secondContext = new SecondContext();
foreach (var first in firstContext.ModelFirstBases){
     var second = new ModelTranslator().TranslateModelFirstBase(first);
     secondContext.ModelFirstSubs.Add(second);
}
secondContext.SaveChanges();
}

但请注意,EF不适用于批量复制数据,即使应该工作也不是一个实际的解决方案。如果使用Sql server或类似的东西,你应该考虑使用SqlBulkCopy来处理你可能使用的任何数据库。

SQLBulkCopy:How does SqlBulkCopy Work

答案 1 :(得分:0)

您是否考虑过第三方库,例如AutoMapperValueInjector

它们似乎是为解决这些类型的挑战而建立的。

您可以轻松执行深度克隆,但如果要保留标识列值,则必须configure EF for identity inserts