我想创建一个集成测试,它从数据库中抓取EF实体,将其克隆到分离的对象,修改它然后将其保存回来并再次将其与原始对象进行比较。
但是,我使用AutoMapper来创建类的克隆,但事实证明它也被跟踪或是原始对象的别名。我需要它与EF完全分离,并且能够在我的存储库类之外执行此操作(即不使用任何EF分离方法)。
这样做的原因是我的EF类包含其他类的嵌套集合,EF不处理持久化整个对象树。因此,我的repository类中的Update()方法处理这个问题,我希望我的NUnit测试来测试这段代码。我希望测试能够在没有EF跟踪的情况下快速创建原始类的副本。
答案 0 :(得分:6)
创建包含当前,原始或数据库的克隆对象 values从CurrentValues返回的DbPropertyValues对象, 可以使用OriginalValues或GetDatabaseValues创建克隆 实体。此克隆将包含来自的属性值 用于创建它的DbPropertyValues对象。例如:
using (var context = new UnicornsContext()) { var unicorn = context.Unicorns.Find(1); var clonedUnicorn = context.Entry(unicorn).GetDatabaseValues().ToObject(); }
请注意,返回的对象不是实体,也不是 跟踪上下文。返回的对象也没有 关系设置为其他对象。
克隆的对象可用于解决与之相关的问题 对数据库的并发更新,尤其是UI所在的位置 涉及到正在使用某种类型的对象的数据绑定。 (看到 关于处理乐观并发的更多细节的第9部分。)
希望它可以帮助别人
答案 1 :(得分:3)
一旦您使用EF 5+,他们引入了AsNoTracking()方法,所有问题都消失了。 下面的行返回一个未链接的实例,因此所有上下文都不会知道该实例中的任何更改:
context.Clients.AsNoTracking().FirstOrDefault(item => item.Id == id);
如果客户引用地址并且您想要一个未链接的实例,只需使用Include:
context.Clients
.Include("Address").AsNoTracking()
.FirstOrDefault(item => item.Id == id);
答案 2 :(得分:0)
如果是测试,您可以执行任何操作,并且不必绑定到任何体系结构方法(如存储库)。您的存储库可能会接收上下文作为注入,因此您可以访问它。另一点是我不相信AutoMapper会创建跟踪实体。
制作类副本的一种方法是使用序列化,默认情况下只保存公共字段(Xml序列化或DataContract序列化)。序列化对象并将其反序列化为新实例。序列化将保存整个对象图,并且反序列化的对象图将被分离。请注意,这些序列化不喜欢对象图中的循环引用(从A到B的导航属性以及从循环到B到A的导航属性)。序列化也具有太强烈的侵略性,因此它可以更深入地遍历图形 - 这在多对多关系中尤其危险。
最好的方法是使用ICloneable
接口并实现Clone
或定义支持方法,这些方法将执行具有所需深度的不同克隆。
Here是克隆基于EntityObject
的实体的另一种方法。这是一个很难的代码,特别是Reflection.Emit
的一部分。但这对你没有帮助,因为代码优先使用的是POCO。