Example E4606
Visible to All Users

How to create persistent classes mapped to tables with a composite primary key at runtime

Files to look at:

When you have a table with a composite key, the standard solution to map an XPO persistent class to it is to declare a structure for the key member as described in the How to create a persistent object for a database table with a compound key article. If the database schema is not known as design time, and you need to create persistent classes at runtime, the built-in XPDictionary methods won't help, because structure-like members are supported only for real types via Reflection. This example demonstrates how to create a custom XPCustomMemberInfo descendant to support nested persistent members.

See also:
K18482: How to create persistent metadata on the fly and load data from an arbitrary table
XPMemberInfo

Does this example address your development requirements/objectives?

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

Example Code

XpoConsoleApplication/Program.cs
C#
using System; using System.Collections.Generic; using System.Text; using DevExpress.Xpo; using DevExpress.Xpo.Metadata; using DevExpress.Data.Filtering; namespace XpoConsoleApplication { class Program { static void Main(string[] args) { XPDictionary dict = new ReflectionDictionary(); //Create dynamic classes and members XPClassInfo ciEngineer = dict.CreateClass("Engineer"); ciEngineer.CreateMember("UserID", typeof(string), new KeyAttribute(), new SizeAttribute(8)); ciEngineer.CreateMember("PublicName", typeof(string), new SizeAttribute(32)); XPClassInfo ciProject = dict.CreateClass("Project"); ciProject.CreateMember("ProjectID", typeof(string), new KeyAttribute(), new SizeAttribute(4)); ciProject.CreateMember("Title", typeof(string), new SizeAttribute(255)); ciProject.CreateMember("Description", typeof(string), new SizeAttribute(SizeAttribute.Unlimited)); ciProject.CreateMember("Assignments", typeof(XPCollection), true, new AssociationAttribute("R1", null, "Assignment")); XPClassInfo ciAssignment = dict.CreateClass("Assignment"); XPComplexCustomMemberInfo miAssignmentKey = new XPComplexCustomMemberInfo(ciAssignment, "Key", typeof(object), new KeyAttribute()); miAssignmentKey.AddSubMember("Engineer", ciEngineer, new PersistentAttribute("Engineer")); miAssignmentKey.AddSubMember("Project", ciProject, new PersistentAttribute("Project"), new AssociationAttribute("R1")); ciAssignment.CreateMember("Role", typeof(string), new SizeAttribute(32)); ciAssignment.CreateMember("Rank", typeof(int)); XPClassInfo ciTask = dict.CreateClass("Task"); XPComplexCustomMemberInfo miTaskKey = new XPComplexCustomMemberInfo(ciTask, "Key", typeof(object), new KeyAttribute()); miTaskKey.AddSubMember("Project", ciProject, new PersistentAttribute("Project")); miTaskKey.AddSubMember("TaskNo", typeof(int), new PersistentAttribute("TaskNo")); ciTask.CreateMember("Description", typeof(string), new SizeAttribute(SizeAttribute.Unlimited)); //Initialize the data layer //XpoDefault.DataLayer = XpoDefault.GetDataLayer(DevExpress.Xpo.DB.MSSqlConnectionProvider.GetConnectionString("(local)", "E4606"), dict, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema); XpoDefault.DataLayer = new SimpleDataLayer(dict, new DevExpress.Xpo.DB.InMemoryDataStore()); //Create DB and some sample data using (UnitOfWork uow = new UnitOfWork()) { uow.UpdateSchema(); IList<object> engineers = CreateObjects(uow, ciEngineer, new string[] { "UserID", "PublicName" }, new object[][] { new object[]{ "U1", "John" }, new object[]{ "PAUL", "Paul" }, new object[]{ "XFR", "Fred" }, }); IList<object> projects = CreateObjects(uow, ciProject, new string[] { "ProjectID", "Title", "Description" }, new object[][] { new object[]{ "P1", "DemoApp", "bla-bla-bla" }, new object[]{ "P2", "MegaApp", "duh!" }, new object[]{ "PNEW", "KillerApp", "wow!" }, }); IList<object> assignments = CreateObjects(uow, ciAssignment, new string[] { "Key.Engineer", "Key.Project", "Role", "Rank" }, new object[][] { new object[]{ engineers[1], projects[0], "Developer", 6 }, new object[]{ engineers[2], projects[0], "Developer", 8 }, new object[]{ engineers[0], projects[1], "Leader", 10 }, new object[]{ engineers[2], projects[1], "Developer", 8 }, new object[]{ engineers[1], projects[1], "Consultant", 3 }, new object[]{ engineers[1], projects[2], "Developer", 8 }, new object[]{ engineers[2], projects[2], "Developer", 7 }, new object[]{ engineers[0], projects[2], "Tester", 5 }, }); IList<object> tasks = CreateObjects(uow, ciTask, new string[] { "Key.Project", "Key.TaskNo", "Description" }, new object[][] { new object[]{ projects[0], 1, "Start" }, new object[]{ projects[0], 2, "Finish" }, new object[]{ projects[1], 0, "Framework" }, new object[]{ projects[1], 1, "Feature1" }, new object[]{ projects[1], 2, "Feature2" }, new object[]{ projects[1], 3, "Feature3" }, new object[]{ projects[1], 4, "Feature4" }, new object[]{ projects[1], 5, "Feature5" }, new object[]{ projects[1], 6, "Polish" }, new object[]{ projects[2], 1, "ready" }, new object[]{ projects[2], 2, "steady" }, new object[]{ projects[2], 3, "go" }, }); uow.CommitChanges(); } //read data using (UnitOfWork uow = new UnitOfWork()) { // XPView xpv = new XPView(uow, ciTask); xpv.Sorting.Add(new SortProperty("Project", DevExpress.Xpo.DB.SortingDirection.Ascending)); xpv.Sorting.Add(new SortProperty("No", DevExpress.Xpo.DB.SortingDirection.Ascending)); xpv.AddProperty("Project", "Key.Project.Title"); xpv.AddProperty("No", "Key.TaskNo"); xpv.AddProperty("Task", "Description"); foreach(ViewRecord r in xpv){ StringBuilder sb = new StringBuilder(); foreach (ViewProperty p in xpv.Properties) { if (sb.Length > 0) sb.Append(", "); sb.Append(r[p.Name]); } Console.WriteLine(sb.ToString()); } Console.WriteLine(); } using (UnitOfWork uow = new UnitOfWork()) { // XPCollection xpc = new XPCollection(uow, ciProject); foreach (object o in xpc) { Console.WriteLine(DumpValues(ciProject, o, new string[] { "Title", "Description" })); XPCollection xpc2 = ciProject.GetMember("Assignments").GetValue(o) as XPCollection; foreach (object o2 in xpc2) { Console.WriteLine(" " + DumpValues(ciAssignment, o2, new string[] { "Role", "Rank", "Key.Engineer.PublicName" })); } } Console.WriteLine(); } using (UnitOfWork uow = new UnitOfWork()) { // object project = uow.FindObject(ciProject, CriteriaOperator.Parse("ProjectID=?", "P2")); DevExpress.Xpo.Helpers.IdList taskKey = new DevExpress.Xpo.Helpers.IdList(); taskKey.Add(project); taskKey.Add(2); object task = uow.GetObjectByKey(ciTask, taskKey); Console.WriteLine(DumpValues(ciTask, task, new string[] { "Key.Project.Title", "Key.TaskNo", "Description" })); Console.WriteLine(); } Console.ReadLine(); } private static IList<object> CreateObjects(Session s, XPClassInfo ci, string[] props, object[][] objValues) { IList<object> list = new List<object>(); foreach (object[] values in objValues) { object o = ci.CreateNewObject(s); for (int i = 0; i < props.Length; i++) { ci.GetMember(props[i]).SetValue(o, values[i]); } s.Save(o); list.Add(o); } return list; } private static string DumpValues(XPClassInfo ci, object o, string[] props) { StringBuilder sb = new StringBuilder(); foreach (string p in props) { if (sb.Length > 0) sb.Append(", "); object v = new DevExpress.Data.Filtering.Helpers.ExpressionEvaluator(ci.GetEvaluatorContextDescriptor(), new DevExpress.Data.Filtering.OperandProperty(p)).Evaluate(o); sb.Append(v); } return sb.ToString(); } } [NonPersistent] public class LiteDataObject : XPLiteObject { public LiteDataObject(Session s) : base(s) { } public LiteDataObject(Session s, XPClassInfo ci) : base(s, ci) { } } }
XpoConsoleApplication/XPComplexCustomMemberInfo.cs
C#
using System; using System.Collections; using System.Collections.Generic; using DevExpress.Xpo.Metadata; namespace DevExpress.Xpo.Metadata { public class XPComplexCustomMemberInfo : XPCustomMemberInfo { public XPComplexCustomMemberInfo(XPClassInfo owner, string propertyName, Type propertyType, params Attribute[] attributes) : this(owner, propertyName, propertyType, null, false, false, attributes) { } public XPComplexCustomMemberInfo(XPClassInfo owner, string propertyName, XPClassInfo referenceType, params Attribute[] attributes) : this(owner, propertyName, null, referenceType, false, false, attributes) { } public XPComplexCustomMemberInfo(XPClassInfo owner, string propertyName, Type propertyType, XPClassInfo referenceType, bool nonPersistent, bool nonPublic, params Attribute[] attributes) : base(owner, propertyName, propertyType, referenceType, nonPersistent, nonPublic) { if (Equals(this.subMembersArray, XPMemberInfo.EmptyList)) { this.subMembersArray = new List<XPMemberInfo>(); } for (int i = 0; i < attributes.Length; i++) { Attribute attribute = attributes[i]; this.AddAttribute(attribute); } } public override bool IsStruct { get { return this.SubMembers.Count > 0; } } public void AddSubMember(XPComplexCustomMemberInfo memeberInfo) { this.SubMembers.Add(memeberInfo); memeberInfo.valueParent = this; } public XPComplexCustomMemberInfo AddSubMember(string propertyName, Type propertyType, params Attribute[] attributes) { XPComplexCustomMemberInfo memeberInfo = new XPComplexCustomMemberInfo(this.Owner, propertyName, propertyType, attributes); AddSubMember(memeberInfo); return memeberInfo; } public XPComplexCustomMemberInfo AddSubMember(string propertyName, XPClassInfo referenceType, params Attribute[] attributes) { XPComplexCustomMemberInfo memeberInfo = new XPComplexCustomMemberInfo(this.Owner, propertyName, referenceType, attributes); AddSubMember(memeberInfo); return memeberInfo; } public override string Name { get { if (valueParent == null) return base.Name; return string.Concat(this.valueParent.Name, '.', base.Name); } } protected override string GetDefaultMappingField() { if (this.valueParent == null) return base.Name; return this.valueParent.MappingField + base.Name; } public override object GetValue(object theObject) { if (IsStruct) { DevExpress.Xpo.Helpers.IdList idList = new DevExpress.Xpo.Helpers.IdList(); foreach (XPMemberInfo memberInfo in this.SubMembers) { if (memberInfo.IsPersistent) { idList.Add(memberInfo.GetValue(theObject)); } } return idList; } return base.GetValue(theObject); } public override void SetValue(object theObject, object theValue) { if (IsStruct) { IList list = theValue as IList; if (list != null) { for (int i = 0; i < list.Count; i++) { XPMemberInfo memberInfo = (XPMemberInfo)this.SubMembers[i]; if (memberInfo.IsPersistent) { memberInfo.SetValue(theObject, list[i]); } } } return; } base.SetValue(theObject, theValue); } } }

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.