Scenario
In this example, we create a CloneIXPSimpleObjectHelper class that will be used to clone the provided persistent object.
Steps to implement
1. Implement the CloneIXPSimpleObjectHelper class as shown in the CloneIXPSimpleObjectHelper.xx file. This class uses persistent object metadata to create a copy of the provided persistent object.
The Clone method has several constructors that allow you to specify the target Session and determine whether object synchronization is required or not. If the synchronize parameter is set to true, reference properties are only cloned if the reference object does not exist in the target Session. Otherwise, the existing object will be reused and synchronized with the source. Set this property to false if the target Session does not contain an object of the source.
2. Create a Form that will use the ListBox control to display the result of creating and cloning persistent objects. See the code of the Form class in the Form1.xx file.
As a result, the following output will be shown:
See also:
How to get a list of a persistent object's properties
Should I use XPO to transfer data between databases?
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 PMI 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;
}
}
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 AMTo 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 disable this part of code to disable creating of additional records.