public static void DeepClone(Entity objectToClone, JTS2Entities mgr, Action<Entity> callBack)
{
Dictionary<EntityKey, Entity> clonedItems = new Dictionary<EntityKey, Entity>();
DeepClone( objectToClone, mgr, clonedItems, callBack);
}
private static void DeepClone(Entity objectToClone, JTS2Entities mgr, Dictionary<EntityKey, Entity> clonedItems, Action<Entity> callBack)
{
((IMyCloningMethods) objectToClone).GetEntityGraphAsync((list) =>
{
//set up clones
foreach (Entity relatedItem in list)
{
Entity relatedClone=(Entity) ((ICloneable) relatedItem).Clone();
MakeUnique((IMyCloningMethods) relatedClone);
mgr.AddEntity(relatedClone);
clonedItems.Add(relatedItem.EntityAspect.EntityKey, relatedClone);
}
//fix up IDs
foreach (Entity relatedItem in list)
{
FixForeignKeys(clonedItems[relatedItem.EntityAspect.EntityKey], clonedItems);
}
foreach (Entity relatedItem in list)
{
RelinkRelations(relatedItem, clonedItems[relatedItem.EntityAspect.EntityKey], clonedItems);
}
callBack(clonedItems[objectToClone.EntityAspect.EntityKey]);
});
}
private static void FixForeignKeys(Entity clone, Dictionary<EntityKey, Entity> clonedItems)
{
var props = clone.GetType().GetProperties();
foreach (var prop in props)
{
var attr = prop.GetCustomAttributes(typeof(RelationPropertyAttribute), true);
if (attr.Length > 0)
{
if (((RelationPropertyAttribute)attr[0]).QueryDirection == QueryDirection.ToRole2)
{
if (!IsSubclassOfRawGeneric(typeof(RelatedEntityList<>), prop.PropertyType))
{
EntityKey key = ((Entity)prop.GetGetMethod().Invoke(clone, null)).EntityAspect.EntityKey;
if (clonedItems.ContainsKey(key))
{
var newRelatedItem = clonedItems[key];
prop.GetSetMethod().Invoke(clone, new object[] { newRelatedItem });
}
}
}
}
}
}
private static void RelinkRelations(Entity objectToClone, Entity clone, Dictionary<EntityKey, Entity> clonedItems)
{
var props = objectToClone.GetType().GetProperties();
foreach (var prop in props)
{
if (IsSubclassOfRawGeneric(typeof(RelatedEntityList<>), prop.PropertyType))
{
IList listToClone = (IList)prop.GetGetMethod().Invoke(objectToClone, null);
IList listOfClone = (IList)prop.GetGetMethod().Invoke(clone, null);
listOfClone.Clear();
if (listToClone != null)
{
//Need an umboundList because the list could be updated by the addition of new clones e.g. in many to many relationships
List<Entity> unboundListToClone = new List<Entity>();
foreach (var listItem in listToClone)
{
if (listItem is Entity)
{
unboundListToClone.Add((Entity)listItem);
}
}
foreach (var listItem in unboundListToClone)
{
Entity relatedObject;
if (clonedItems.ContainsKey(listItem.EntityAspect.EntityKey))
{
relatedObject = clonedItems[listItem.EntityAspect.EntityKey];
}
else
{
relatedObject = listItem;
}
listOfClone.Add(relatedObject);
}
}
}
}
}
static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
{
while (toCheck != typeof(object))
{
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur)
{
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
static private void MakeUnique(IMyCloningMethods entity)
{
foreach (var propName in entity.FieldsWithUniqueConstraint) //this is a list of field names (string) that I maintain manually in code
{
var prop = entity.GetType().GetProperty(propName);
//make name unique then set it back to the property
object[] parameters = {prop.GetGetMethod().Invoke(entity, null) + " copied on " + DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss")};
prop.GetSetMethod().Invoke(entity, parameters);
}
}