为什么,在 Entity Framework 6 中,当将域模型复制到另一个域模型时,原始对象是否会在更改副本的值时更新?

Why, in Entity Framework 6, when copying a domain model to another domain model, does the original object get updated when changing the copy's values?

以下是此示例所需的模型。

public class OrderDetailPackageVM
{
    public OrderDetail OrderDetail { get; set; }
    public Package Package { get; set; }
}

public class Package
{
    public Package()
    {
        this.PackageProducts = new List<PackageProduct>();
    }

    public int PackageId { get; set; }

    public int WebsiteId { get; set; }

    public virtual List<PackageProduct> PackageProducts { get; set; }

}

public class PackageProduct
{
    public int PackageProductId { get; set; }

    public int PackageId { get; set; }

    public virtual Package Package { get; set; }

    public int ProductId { get; set; }

    public virtual Product Product { get; set; }

    public int ProductCategoryId { get; set; } // not a FK but data only

    public virtual ProductCategory ProductCategory { get; set; }
}

在下面的代码片段中,您应该会看到说明的问题。

List<OrderDetailPackageVM> pkgs = (from odx in db.OrderDetails
                                               from pax in db.Packages
                                               where odx.OrderId == orderId
                                               && pax.PackageId == odx.PackageId
                                               && odx.PricelistProduct.Product.isStandalone == true
                                               && pax.WebsiteId == websiteId
                                               select new OrderDetailPackageVM
                                               {
                                                   Package = pax,
                                                   OrderDetail = odx
                                               }).AsNoTracking().ToList();


List<OrderDetailPackageVM> packages = new List<OrderDetailPackageVM>();
packages.AddRange(pkgs);

//also tried packages = pkgs;
//also tried packages.injectFrom(pkgs) //from omu valueInjector - similar to automapper

此时在我的手表中我们看到: pkgs.Package.PackageProducts.Count = 6; packages.Package.PackageProducts.Count = 6;

foreach (OrderDetailPackageVM pac in packages)
{
    pac.Package.PackageProducts.RemoveAll();
}

此时在我的手表中我们看到:

pkgs.Package.PackageProducts.Count = 0;
packages.Package.PackageProducts.Count = 0;

当我期待看到:

pkgs.Package.PackageProducts.Count = 6;
packages.Package.PackageProducts.Count = 0;
  1. 那么为什么在将更改应用到副本时原始对象会发生变化。我不记得早期版本的 EF 中有这种行为?

  2. 解决这个问题的方法是什么?

  3. 我认为使用 NoTracking 进行 select 应该 'Free' 来自 EF 更改跟踪的模型中的数据?

非常感谢您帮助我理解这种行为。

以下是我根据以下反馈解决此问题的方法:

public static T DeepClone<T>(this T source)
    {
        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
    }

您没有创建新对象。您将现有对象放在新列表中。您需要创建全新的对象并手动复制值。这也称为深层复制或克隆(参见 ICloneable)。

如果 omu valueInjector 将 属性 分配给 属性,它会将对象 a 的列表分配给对象 b 的列表。因为是引用类型,所以其实是一样的。如果您想拥有新对象,则必须进行深拷贝。有关详细信息,请参阅 Deep cloning objects

该行为实际上与从 EF 视图跟踪更改无关。您使用引用类型。

一个小示例程序:

using System;
using System.Collections.Generic;

namespace _28637405 {

  class Outer {
    public string MyProperty { get; set; }
  }

  class Program {
    static void Main( string[] args ) {
      var listOne = new List<Outer>();
      for ( int i = 0; i < 10; i++ ) {
        listOne.Add( new Outer { MyProperty = "obj #" + (i + 1) } );
      }
      // first line
      Console.WriteLine( listOne[0].MyProperty );
      var listTwo = new List<Outer>();
      listTwo.AddRange( listOne );
      // second line
      Console.WriteLine( listTwo[0].MyProperty );
      listTwo[0].MyProperty = "Changed";
      // third and fourth line
      Console.WriteLine( listOne[0].MyProperty );
      Console.WriteLine( listTwo[0].MyProperty );
      var listThree = new List<Outer>();
      foreach ( var obj in listOne )
        listThree.Add( new Outer { MyProperty = obj.MyProperty } );
      listThree[0].MyProperty += " again";
      // lines 5,6,7
      Console.WriteLine( listOne[0].MyProperty );
      Console.WriteLine( listTwo[0].MyProperty );
      Console.WriteLine( listThree[0].MyProperty );
    }
  }
}

它产生的输出:

obj #1
obj #1
Changed
Changed
Changed
Changed
Changed again

class 外部如果实现 ICloneable:

看起来像这样
class Outer : ICloneable {
  public string MyProperty { get; set; }

  public object Clone() {
    return new Outer { MyProperty = this.MyProperty };
  }
}

用法是(包括转换为 Outer ):

var newObject = existingObject.Clone() as Outer;