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
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) { }
}
}
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);
}
}
}