Question Comments
Added By:
Sigurd Decroos at:
1/8/2013 5:48:41 PM There's a little catch with this code. Primary Keys are changed, so it doesn't make a real clone, but a copy. You have to change the code a bit so the primary key gets synced too if needed.
Added By:
Mr292 at:
2/16/2013 12:45:27 PM I refactored some in order to replicate to different databases as well as to different assemblies in the other database. I came about this requirement because I wanted to remodel my DOM and did not want to have to manually do a lot of data imports.
public class CloneIXPSimpleObjectHelper
{
/// <summary>
/// A dictionary containing objects from the source session as key and objects from the
/// target session as values
/// </summary>
/// <returns></returns>
Dictionary<object, object> clonedObjects;
Session sourceSession;
UnitOfWork targetSession;
/// <summary>
/// Initializes a new instance of the CloneIXPSimpleObjectHelper class.
/// </summary>
public CloneIXPSimpleObjectHelper(Session source, UnitOfWork target)
{
clonedObjects = new Dictionary<object, object>();
sourceSession = source;
targetSession = target;
}
/// <summary>
/// Initializes a new instance of the CloneIXPSimpleObjectHelper class to Clone to Different DB.
/// </summary>
public CloneIXPSimpleObjectHelper(Session session, string connstring)
{
UnitOfWork NewUOW = new UnitOfWork(
XpoDefault.GetDataLayer(connstring, DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists));
clonedObjects = new Dictionary<object, object>();
sourceSession = session;
targetSession = NewUOW;
}
/// <summary>
/// Initializes a new instance of the CloneIXPSimpleObjectHelper class to Clone to Different Assembly / DB.
/// </summary>
public CloneIXPSimpleObjectHelper(Session session, string connstring, System.Reflection.Assembly assembly)
{
var dict = new ReflectionDictionary();
foreach (var item in assembly.GetTypes())
{
if (item.IsSubclassOf(typeof(XPBaseObject)))
dict.CollectClassInfos(item);
}
UnitOfWork NewUOW = new UnitOfWork(
XpoDefault.GetDataLayer(connstring, dict,
DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists));
clonedObjects = new Dictionary<object, object>();
sourceSession = session;
targetSession = NewUOW;
}
public void Commit()
{
targetSession.CommitTransaction();
}
public T Clone<T>(T source) where T : IXPSimpleObject
{
return Clone<T>(source, targetSession, CloneMethod.IgnoreIfExistsInDesitnation);
}
public T Clone<T>(T source, CloneMethod sync) where T : IXPSimpleObject
{
return (T)Clone(source as IXPSimpleObject, targetSession, sync);
}
public object Clone(IXPSimpleObject source)
{
return Clone(source, targetSession, CloneMethod.IgnoreIfExistsInDesitnation);
}
public object Clone(IXPSimpleObject source, CloneMethod sync)
{
return Clone(source as IXPSimpleObject, targetSession, sync);
}
public object Clone(IXPSimpleObject source, Session targetSession, CloneMethod sync)
{
return Clone(source as IXPSimpleObject, targetSession, sync);
}
public T Clone<T>(T source, UnitOfWork targetSession, CloneMethod sync) where T : IXPSimpleObject
{
return (T)Clone(source as IXPSimpleObject, targetSession, sync);
}
public object Clone(XPBaseObject obj, Type targetType, CloneMethod sync)
{
return Clone(obj, targetSession, targetSession.GetClassInfo(targetType), sync);
}
public enum CloneMethod
{
TransferOnly,
Synchronize,
IgnoreIfExistsInDesitnation
}
/// <summary>
/// Clones and / or synchronizes the given IXPSimpleObject.
/// </summary>
/// <param name="source"></param>
/// <param name="targetSession"></param>
/// <param name="synchronize">If set to true, reference properties are only cloned in case
/// the reference object does not exist in the targetsession. Otherwise the exising object will be
/// reused and synchronized with the source. Set this property to false when knowing at forehand
/// that the targetSession will not contain any of the objects of the source.</param>
/// <returns></returns>
public object Clone(IXPSimpleObject source, UnitOfWork parentSession, XPClassInfo cloneClassInfo, CloneMethod sync)
{
if (source == null)
return null;
object sourceKey = source.Session.GetKeyValue(source);
if (clonedObjects.ContainsKey(source))
return parentSession.GetObjectByKey(cloneClassInfo,cloneClassInfo.KeyProperty.GetValue(clonedObjects[source]));
NestedUnitOfWork nestedSession = parentSession.BeginNestedUnitOfWork();
IXPSimpleObject clone = null;
if (sync != CloneMethod.TransferOnly)
{
clone = (IXPSimpleObject)nestedSession.GetObjectByKey(cloneClassInfo, sourceKey);
if (clone != null)
{
if (sync == CloneMethod.IgnoreIfExistsInDesitnation)
{
clonedObjects.Add(source, clone);
return nestedSession.GetParentObject(clone);
}
}
}
if (clone == null)
clone = (IXPSimpleObject)cloneClassInfo.CreateNewObject(nestedSession);
clonedObjects.Add(source, clone);
try
{
XPClassInfo sourceClassInfo = source.Session.GetClassInfo(source.GetType());
if (sourceClassInfo.KeyProperty.GetType() == cloneClassInfo.KeyProperty.GetType())
cloneClassInfo.KeyProperty.SetValue(clone, sourceKey);
foreach (XPMemberInfo cloneMember in cloneClassInfo.PersistentProperties)
{
XPMemberInfo sourceMem = sourceClassInfo.GetMember(cloneMember.Name);
if (sourceMem == null
|| cloneMember is DevExpress.Xpo.Metadata.Helpers.ServiceField
|| cloneMember.IsKey)
continue;
object val = null;
if (cloneMember.ReferenceType != null)
{
object createdByClone = cloneMember.GetValue(clone);
if (createdByClone != null)
{
val = Clone((IXPSimpleObject)sourceMem.GetValue(source),
nestedSession,
nestedSession.GetClassInfo(cloneMember.MemberType),
sync==CloneMethod.TransferOnly?CloneMethod.Synchronize:sync);
}
else
if ((cloneMember.IsAggregated)
//REMOVE Below LINE IF YOU WANT TO MAINTAIN ONLY ONE WAY AGGREGATIOn
|| (cloneMember.IsAssociation
&& cloneMember.GetAssociatedMember().IsAggregated)
)
{
val = Clone((IXPSimpleObject)sourceMem.GetValue(source), nestedSession,
nestedSession.GetClassInfo(cloneMember.MemberType), sync);
}
}
else
{
val = sourceMem.GetValue(source);
}
if ((val != null) && !(val is DateTime && (DateTime)val == DateTime.MinValue))
cloneMember.SetValue(clone, val);
}
foreach (XPMemberInfo m in cloneClassInfo.CollectionProperties)
{
if (m.HasAttribute(typeof(AggregatedAttribute)))
{
XPBaseCollection col = (XPBaseCollection)m.GetValue(clone);
XPBaseCollection colSource = (XPBaseCollection)sourceClassInfo.GetMember(m.Name).GetValue(source);
foreach (IXPSimpleObject obj in new ArrayList(colSource))
{
XPClassInfo targetinfo = col.GetObjectClassInfo();
col.BaseAdd(
Clone(obj, nestedSession,
targetinfo, sync));
}
}
}
nestedSession.CommitTransaction(); //
clonedObjects[source]= clone;
return nestedSession.GetParentObject(clone);
}
catch
{
if (nestedSession.InTransaction)
nestedSession.RollbackTransaction();
}
return null;
}
}
Added By:
Chris D at:
8/4/2015 9:56:17 AM Dear,
I face a real problem with your clonehelper. After implementing we recoginized that this helper will not only clone my recordset, it will also clone/create my relations.
So, for example i have a order management where i want to clone one order. But what happened is that the order will be cloned, but the underlying addresses (relation via keys) will be cloned, too. So, after cloning 10 orders, i have 10 times the same address in my address-masterdata ?
Any idea ?
Added By:
Chris D at:
8/4/2015 10:50:22 AM To avoid creating the underlying record, i changed
If m.ReferenceType IsNot Nothing And NONEW=1 Then....
where NONEW=0.
Could you please have a short look if this is the right solution ?
Added By:
Alexey (DevExpress Support) at:
8/4/2015 11:14:43 PM Hello Chris,
Yes, it is possible to comment this part of code to disable creating of additional records.Added By:
Paul Kubb at:
1/11/2016 9:00:47 PM Is this equivalent to Extra Clone Module?
Added By:
Alexey (DevExpress Support) at:
1/12/2016 3:32:14 AM You are correct. While the Clone Object Module is specific for an XAF application, this approach allows implementing the same functionality with XPO in other .NET applications.