Example E804
Visible to All Users

How to clone a persistent object

Files to look at:

Scenario

In this example, we create a CloneHelper class that will be used to clone the provided persistent object.

Steps to implement

1.  Implement the CloneHelper class as shown in the CloneHelper.xx  file. This class uses persistent object metadata to create a copy of the provided persistent object.
The Clone method has several overloads that allow you to determine whether object synchronization is required or not. If the synchronize parameter is set to true, reference properties are cloned only if the referenced object does not exist in the target Session. Otherwise, the existing object will be reused.

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?

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

SimpleObject/CloneHelper.cs(vb)
C#
using System; using DevExpress.Xpo; using System.Collections.Generic; using DevExpress.Xpo.Metadata; using System.Collections; namespace SimpleObject { public class CloneHelper : IDisposable { readonly Dictionary<object, object> clonedObjects; readonly Session targetSession; public CloneHelper(Session targetSession) { clonedObjects = new Dictionary<object, object>(); this.targetSession = targetSession; } public T Clone<T>(T source) { return Clone<T>(source, false); } public T Clone<T>(T source, bool synchronize) { return (T)Clone((object)source, synchronize); } public object Clone(object source) { return Clone(source, false); } /// <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(object source, bool synchronize) { if (source == null) return null; XPClassInfo targetClassInfo = targetSession.GetClassInfo(source.GetType()); object target = targetClassInfo.CreateNewObject(targetSession); clonedObjects.Add(source, target); foreach (XPMemberInfo m in targetClassInfo.PersistentProperties) { CloneProperty(m, source, target, synchronize); } foreach (XPMemberInfo m in targetClassInfo.CollectionProperties) { CloneCollection(m, source, target, synchronize); } return target; } private void CloneProperty(XPMemberInfo memberInfo, object source, object target, bool synchronize) { if (memberInfo is DevExpress.Xpo.Metadata.Helpers.ServiceField || memberInfo.IsKey) { return; } object clonedValue = null; if (memberInfo.ReferenceType != null) { object value = memberInfo.GetValue(source); if (value != null) { clonedValue = CloneValue(value, synchronize, false); } } else { clonedValue = memberInfo.GetValue(source); } memberInfo.SetValue(target, clonedValue); } private void CloneCollection(XPMemberInfo memberInfo, object source, object target, bool synchronize) { if (memberInfo.IsAssociation && (memberInfo.IsManyToMany || memberInfo.IsAggregated)) { XPBaseCollection colTarget = (XPBaseCollection)memberInfo.GetValue(target); XPBaseCollection colSource = (XPBaseCollection)memberInfo.GetValue(source); foreach (IXPSimpleObject obj in colSource) { colTarget.BaseAdd(CloneValue(obj, synchronize, !memberInfo.IsManyToMany)); } } } private object CloneValue(object propertyValue, bool synchronize, bool cloneAlways) { if (clonedObjects.ContainsKey(propertyValue)) { return clonedObjects[propertyValue]; } object clonedValue = null; if (synchronize && !cloneAlways) { clonedValue = targetSession.GetObjectByKey(targetSession.GetClassInfo(propertyValue), targetSession.GetKeyValue(propertyValue)); } if (clonedValue == null) { clonedValue = Clone(propertyValue, synchronize); } return clonedValue; } public void Dispose() { if (targetSession != null) targetSession.Dispose(); } } }
SimpleObject/Form1.cs(vb)
C#
using System; using System.Windows.Forms; using DevExpress.Xpo; using DevExpress.Xpo.DB; namespace SimpleObject { /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : Form { private ListBox listBox1; /// <summary> /// Required designer variable. /// </summary> private readonly System.ComponentModel.Container components = null; public Form1() { InitializeComponent(); } protected override void Dispose(bool disposing) { if(disposing) { if(components != null) { components.Dispose(); } } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.listBox1 = new System.Windows.Forms.ListBox(); this.SuspendLayout(); // // listBox1 // this.listBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.listBox1.Location = new System.Drawing.Point(0, 0); this.listBox1.Name = "listBox1"; this.listBox1.Size = new System.Drawing.Size(413, 277); this.listBox1.TabIndex = 0; // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(413, 277); this.Controls.Add(this.listBox1); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); this.ResumeLayout(false); } #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { XpoDefault.DataLayer = new SimpleDataLayer(new InMemoryDataStore()); Application.Run(new Form1()); } private void Form1_Load(object sender, EventArgs e) { UnitOfWork uow1 = new UnitOfWork(); Parent obj = new Parent(uow1) { Name = "Parent A" }; Child ch1 = new Child(uow1) { Name = "Child 1" }; Child ch2 = new Child(uow1) { Name = "Child 2" }; obj.Children.Add(ch1); obj.Children.Add(ch2); obj.Save(); uow1.CommitChanges(); listBox1.Items.Add(string.Format("Object created: {0}", obj.Name)); UnitOfWork uow2 = new UnitOfWork(); CloneHelper cloneHelper = new CloneHelper(uow2); Parent clone = cloneHelper.Clone<Parent>(obj); listBox1.Items.Add(string.Format("Object cloned: {0}", clone.Name)); listBox1.Items.Add(string.Format("Object's child 1: {0}", clone.Children[0].Name)); listBox1.Items.Add(string.Format("Object's child 2: {0}", clone.Children[1].Name)); } } }
SimpleObject/ObjectClass.cs(vb)
C#
using System; using DevExpress.Xpo; namespace SimpleObject { public class Parent : XPObject { public Parent(Session session) : base(session) { } public override void AfterConstruction() { base.AfterConstruction(); } private string _Name; public string Name { get { return _Name; } set { SetPropertyValue("Name", ref _Name, value); } } [Association("ParentChildren"), Aggregated] public XPCollection<Child> Children { get { return GetCollection<Child>("Children"); } } } public class Child : XPObject { public Child(Session session) : base(session) { } public override void AfterConstruction() { base.AfterConstruction(); } private string _Name; public string Name { get { return _Name; } set { SetPropertyValue("Name", ref _Name, value); } } private Parent _Parent; [Association("ParentChildren")] public Parent Parent { get { return _Parent; } set { SetPropertyValue("Parent", ref _Parent, value); } } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.