这是我的情景。我们有一个Customers表,有各种导航属性(1-1,1-many,many-many等)来说Orders,它们有OrderItems等。(读取非常深的对象图)
很简单的例子: Customer1有订单[1..5],而Customer2有订单[6..9]
现在有一段时间我们发现两个客户实际上是同一个,我们想合并两个: Customer1.Merge(Customer2);
合并后,我们应该看到 Customer1有订单[1..9]以及Customer2的所有其他属性,而且Customer2为空,可以安全删除。
现在我发现了一篇关于深度克隆(http://www.urmanet.ch/?p=11)的相当古老的帖子,结果是新实体,但我有两个现有实体需要减少到已添加第二个实体的对象图的一个现有实体,然后我可以删除第二个实体并提交工作单元。这个例子来源于2008年,现在有了EF的新版本,我想知道正确的方法是什么?
我喜欢扩展方法的想法,该方法通常会执行此任务(意味着反射),以便当/如果实体模型发生更改(添加新属性/关系等),此通用合并方法不会需要改变 ......
这是我开始使用的基本代码,我想修改它以便它可以合并两个实体,而不是创建一个新实体(克隆)。
/// This class is used to store self references for
/// back tracking
public class SelfReferencesTracking
{
public string EntitySetName;
public EntityObject NewEntityObject;
public EntityKey OriginalKeys;
}
/// Extension method class for the EntityObject class
public static class EntityObjectExtension
{
//Enable tracking
private static readonly List<SelfReferencesTracking> Tracking =
new List<SelfReferencesTracking>();
/// These method makes a 1:1 copy of the original entity object
///
/// The original entity object /// The copied entity object
public static EntityObject Clone(this EntityObject entityObject)
{
//Get constructor for new object
object newEntityObject = entityObject.GetType().GetConstructor(new Type[0]).Invoke(new object[0]);
Tracking.Add(new SelfReferencesTracking
{
EntitySetName = entityObject.EntityKey.EntitySetName,
OriginalKeys = entityObject.EntityKey,
NewEntityObject = (EntityObject) newEntityObject
});
//Copy all properties and its values of the given type
PropertyInfo[] properties = entityObject.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
try
{
object propertyValue = property.GetValue(entityObject, null);
PropertyInfo myProperty = property;
if (!entityObject.EntityKey.EntityKeyValues.Any(x => x.Key == myProperty.Name))
{
//Ignore all properties of these types
if (property.PropertyType != typeof (EntityKey) &&
property.PropertyType != typeof (EntityState) &&
property.PropertyType != typeof (EntityReference<>))
{
//Check, if the property is a complex type (collection), in that
//case, some special calls are necessary
if (
property.GetCustomAttributes(typeof (EdmRelationshipNavigationPropertyAttribute), false)
.Count() == 1)
{
//Check for self referencing entities
if (propertyValue.GetType() == entityObject.GetType())
{
//Get the self referenced entity object
var selfRefrencedEntityObject = (EntityObject) property.GetValue(entityObject, null);
//This variable is used to store the new parent entity objects
EntityObject newParentEntityObject = null;
//This loops might be replaced by LINQ queries... I didn't try that
foreach (
SelfReferencesTracking tracking in
Tracking.Where(
x =>
x.EntitySetName == selfRefrencedEntityObject.EntityKey.EntitySetName))
{
//Check, if the key is in the tracking list
foreach (
EntityKeyMember newKeyValues in
selfRefrencedEntityObject.EntityKey.EntityKeyValues)
{
//Iterate trough the keys and values
foreach (
EntityKeyMember orgKeyValues in tracking.OriginalKeys.EntityKeyValues)
{
//The key is stored in the tracking list, which means, this is
//the foreign key used by the self referencing property
if (newParentEntityObject == null)
{
if (orgKeyValues.Key == newKeyValues.Key &&
orgKeyValues.Value == newKeyValues.Value)
{
//Store the parent entity object
newParentEntityObject = tracking.NewEntityObject;
}
}
else
{
break;
}
}
}
}
//Set the value to the new parent entity object
property.SetValue(newEntityObject, newParentEntityObject, null);
}
else
{
//Entity collections are always generic
if (propertyValue.GetType().IsGenericType)
{
//Don't include self references collection, e.g. Orders1, Orders2 etc.
//Check for equality of the types (string comparison)
if (
!propertyValue.GetType()
.GetGenericArguments()
.First()
.FullName.Equals(entityObject.GetType().FullName))
{
//Get the entities of the given property
var entities = (RelatedEnd) property.GetValue(entityObject, null);
//Load underlying collection, if not yet done...
if (!entities.IsLoaded) entities.Load();
//Create a generic instance of the entities collection object
Type t =
typeof (EntityCollection<>).MakeGenericType(new[]
{property.PropertyType.GetGenericArguments()[0]});
object newEntityCollection = Activator.CreateInstance(t);
//Iterate trough the entities collection
foreach (object entity in entities)
{
//Add the found entity to the dynamic generic collection
MethodInfo addToCollection =
newEntityCollection.GetType().GetMethod("Add");
addToCollection.Invoke(newEntityCollection,
new object[] {Clone((EntityObject) entity)});
}
//Set the property value
property.SetValue(newEntityObject, newEntityCollection, null);
}
}
}
}
else
{
var baseType = propertyValue.GetType().BaseType;
if (baseType != null && baseType.Name == "EntityReference")
{
//Simply copy the EntityKey to the new entity object’s EntityReference
((EntityReference) property.GetValue(newEntityObject, null)).EntityKey =
((EntityReference) property.GetValue(entityObject, null)).EntityKey;
}
else
{
//Common task, just copy the simple type property into the new entity object
property.SetValue(
newEntityObject,
property.GetValue(entityObject, null), null);
}
}
}
}
}
catch (InvalidCastException ie)
{
//Hmm, something happend...
Debug.WriteLine(ie.ToString());
continue;
}
catch (Exception ex)
{
//Hmm, something happend...
Debug.WriteLine(ex.ToString());
continue;
}
}
return (EntityObject) newEntityObject;
}
}
回顾一下:我想帮助将这个克隆扩展方法转换为合并扩展方法,我希望专家可以介入并帮助完成图片。非常感谢任何帮助!