preliminary scaffolding for supporting OMS database modeling via .NET classes

This commit is contained in:
Michael Becker 2024-07-21 22:40:14 -04:00
parent 999f05b8b4
commit d69e83c974
Signed by: beckermj
GPG Key ID: 24F8DAA73DCB2C8F
7 changed files with 299 additions and 13 deletions

View File

@ -1,6 +1,19 @@
using System.Diagnostics;
namespace Mocha.Core.Modeling;
public class OmsClass
{
public Guid GlobalIdentifier { get; internal set; } = Guid.Empty;
protected object GetAttributeValue()
{
StackTrace st = new StackTrace();
StackFrame frame = st.GetFrame(0);
return 1;
}
protected void SetAttributeValue(object value)
{
}
}

View File

@ -1,5 +1,7 @@
using System.Globalization;
using System.Reflection;
using MBS.Core.Reflection;
using Mocha.Core.Modeling.PropertyImplementations;
namespace Mocha.Core.Modeling;
@ -13,6 +15,9 @@ public class OmsDatabase
Oms = oms;
_Type = this.GetType();
// First create all the classes we've defined in the database.
// These are nested types of the class derived from OmsDatabase.
Type[] nestedTypes = null;
try
{
@ -33,6 +38,44 @@ public class OmsDatabase
CreateClass(t);
}
foreach (Type t in nestedTypes)
{
if (t == null) continue;
if (t.IsAbstract) continue;
if (!t.IsSubclassOf(typeof(OmsClass)))
continue;
SetupClassProperties(t);
}
// Now that our schema is set up, go through OmsDatabase public properties
// and assign OmsPropertyImplementations to the properties which represent
// Lists of instances.
FieldInfo[] fis = _Type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in fis)
{
if (fi.FieldType.IsSubclassOfGeneric(typeof(ICollection<>)))
{
// this is a collection of instances of the given type, which must inherit from OmsClass
Type genericTypeArg = fi.FieldType.GenericTypeArguments[0];
if (!genericTypeArg.IsSubclassOf(typeof(OmsClass)))
{
throw new InvalidOperationException("Generic type argument of collection type must inherit from OmsClass");
}
fi.SetValue(this, OmsInstanceList.CreateGeneric(this, genericTypeArg));
}
else if (fi.FieldType.IsSubclassOf(typeof(OmsClass)))
{
// pi.SetValue(this, new OmsNonsingularRelationshipProperty());
}
else
{
}
}
}
private void CreateClass(Type t)
@ -53,12 +96,6 @@ public class OmsDatabase
}
}
PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
SetupClassProperty(t, property);
}
if (globalIdentifier == Guid.Empty)
{
globalIdentifier = Guid.NewGuid();
@ -68,8 +105,40 @@ public class OmsDatabase
Oms.CreateClass(className, globalIdentifier);
}
private void SetupClassProperties(Type t)
{
PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
SetupClassProperty(t, property);
}
}
public static Guid GetGlobalIdentifierForClass(Type t)
{
if (!t.IsSubclassOf(typeof(OmsClass)))
{
throw new InvalidOperationException("Must be subclass of OmsClass");
}
object[] attrs = t.GetCustomAttributes(false);
foreach (object attr in attrs)
{
if (attr is OmsGlobalIdentifierAttribute)
{
return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier;
}
}
return Guid.Empty;
}
private void SetupClassProperty(Type t, PropertyInfo pi)
{
if (pi.PropertyType == typeof(Guid) && pi.Name == "GlobalIdentifier")
{
return;
}
object[] propertyAttrs = pi.GetCustomAttributes(false);
Guid globalIdentifier = Guid.Empty;
foreach (object attr in propertyAttrs)
@ -85,8 +154,70 @@ public class OmsDatabase
globalIdentifier = Guid.NewGuid();
}
InstanceHandle relationshipInstance = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier);
ApplyNativeAttributes(relationshipInstance, propertyAttrs);
Guid myGuid = GetGlobalIdentifierForClass(t);
InstanceHandle sourceClassInstance = Oms.GetInstance(myGuid);
InstanceHandle propertyInstance = InstanceHandle.Empty;
if (pi.PropertyType.IsSubclassOfGeneric(typeof(ICollection<>)))
{
Type arg = pi.PropertyType.GenericTypeArguments[0];
Guid guid = GetGlobalIdentifierForClass(arg);
InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid);
CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, false);
}
else if (pi.PropertyType.IsSubclassOf(typeof(OmsClass)))
{
Guid guid = GetGlobalIdentifierForClass(pi.PropertyType);
InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid);
CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, true);
}
else
{
// attribute
if (pi.PropertyType == typeof(string))
{
// Text Attribute
CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute), pi.Name);
}
else if (pi.PropertyType == typeof(bool))
{
// Boolean Attribute
CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute), pi.Name);
}
else if (pi.PropertyType == typeof(decimal)
|| pi.PropertyType == typeof(int))
{
// Numeric Attribute
CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute), pi.Name);
}
else if (pi.PropertyType == typeof(DateTime))
{
// Date Attribute
CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute), pi.Name);
}
else
{
throw new InvalidOperationException(String.Format("OMS type of property with CLR type '{0}' could not be determined", pi.PropertyType));
}
}
ApplyNativeAttributes(propertyInstance, propertyAttrs);
}
private void CreateAttribute(out InstanceHandle propertyInstance, InstanceHandle attributeClassInstance, string name)
{
propertyInstance = Oms.CreateInstanceOf(attributeClassInstance);
Oms.SetAttributeValue(propertyInstance, Oms.GetInstance(KnownAttributeGuids.Text.Name), name);
}
private void CreateRelationship(out InstanceHandle relationshipInstance, Guid globalIdentifier, InstanceHandle sourceClassInstance, InstanceHandle relationshipDestinationClassInstance, bool relationshipSingular)
{
relationshipInstance = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier);
Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_source__Class), sourceClassInstance);
Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class), relationshipDestinationClassInstance);
Oms.SetAttributeValue(relationshipInstance, Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), relationshipSingular);
}
private void ApplyNativeAttributes(InstanceHandle inst, object[] propertyAttrs)

View File

@ -0,0 +1,96 @@
using System.Collections;
namespace Mocha.Core.Modeling;
public class OmsInstanceList
{
internal static OmsInstanceList CreateGeneric(OmsDatabase omsdb, Type type)
{
Guid gl = OmsDatabase.GetGlobalIdentifierForClass(type);
Type t = typeof(OmsInstanceList<>);
Type concrete = t.MakeGenericType(new Type[] { type });
return (OmsInstanceList) concrete.Assembly.CreateInstance(concrete.FullName, false, System.Reflection.BindingFlags.Default, null, new object[] { omsdb, gl }, null, null);
}
}
public class OmsInstanceList<T> : OmsInstanceList, IList<T> where T : OmsClass
{
private OmsDatabase omsdb;
private Guid classGuid;
public OmsInstanceList(OmsDatabase omsdb, Guid classGuid)
{
this.omsdb = omsdb;
this.classGuid = classGuid;
}
public T this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int Count
{
get
{
InstanceHandle ih = omsdb.Oms.GetInstance(classGuid);
return omsdb.Oms.CountInstances(ih);
}
}
public bool IsReadOnly => throw new NotImplementedException();
public void Add(T item)
{
if (item.GlobalIdentifier == Guid.Empty)
{
Guid localClassGuid = OmsDatabase.GetGlobalIdentifierForClass(item.GetType());
Guid localInstanceGuid = Guid.NewGuid();
omsdb.Oms.CreateInstanceOf(omsdb.Oms.GetInstance(localClassGuid), localInstanceGuid);
}
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
public int IndexOf(T item)
{
throw new NotImplementedException();
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,5 @@
namespace Mocha.Core.Modeling.PropertyImplementations;
public class OmsNonsingularRelationshipProperty : OmsRelationshipProperty
{
}

View File

@ -0,0 +1,6 @@
namespace Mocha.Core.Modeling.PropertyImplementations;
public abstract class OmsRelationshipProperty
{
}

View File

@ -116,12 +116,19 @@ public class MiniOms : MemoryOms
CreateRelationship(c_Class, "has", c_Attribute, KnownRelationshipGuids.Class__has__Attribute, false, "for", KnownRelationshipGuids.Attribute__for__Class);
AddAttribute(c_Class, a_Name);
AddAttribute(c_Attribute, a_Name);
AddAttribute(c_Relationship, a_RelationshipType);
AddAttribute(c_Relationship, a_Singular);
//* ========== Superclass / Subclass Relationships ========== *//
CreateRelationship(c_Class, "has super", c_Class, KnownRelationshipGuids.Class__has_super__Class, false, "has sub", KnownRelationshipGuids.Class__has_sub__Class);
AddSuperClass(c_TextAttribute, c_Attribute);
AddSuperClass(c_BooleanAttribute, c_Attribute);
AddSuperClass(c_NumericAttribute, c_Attribute);
AddSuperClass(c_DateAttribute, c_Attribute);
c_WorkSet = CreateClass("Work Set", KnownInstanceGuids.Classes.WorkSet);
SetInstanceKey(c_WorkSet, new InstanceKey(1, 15));
AddAttribute(c_WorkSet, a_Name);

View File

@ -9,19 +9,36 @@ public class VehicleForHireDB : OmsDatabase
[OmsName("Business"), OmsGlobalIdentifier("{c4e1d676-726c-42f4-9edb-a0ca0ee0e612}")]
public class Business : OmsClass
{
public Business(string name)
{
Name = name;
}
[OmsGlobalIdentifier("{9153A637-992E-4712-ADF2-B03F0D9EDEA6}")]
public readonly string Name;
/// <summary>
/// Business.has Driver
/// </summary>
/// <value></value>
[OmsRelationshipType("has"), OmsGlobalIdentifier("{63e5ab3a-4009-4af9-8171-5b23472c1c8c}")]
public OmsRelationship<Business, Driver> Drivers { get; }
public readonly IList<Driver> Drivers;
/// <summary>
/// Business.has Vehicle
/// </summary>
/// <value></value>
[OmsRelationshipType("has"), OmsGlobalIdentifier("{c54125f6-c3b0-40b2-99f9-e0ed20a75bfa}")]
public OmsRelationship<Business, Driver> Vehicles { get; }
public readonly IList<Vehicle> Vehicles;
[OmsRelationshipType("has"), OmsGlobalIdentifier("{9d4946c3-db8d-4f27-b21b-52e9b0fdf231}")]
public BusinessPermit Permit;
}
[OmsName("Business Permit"), OmsGlobalIdentifier("{7680b302-c9c2-4275-abf8-373adafbf58e}")]
public class BusinessPermit : OmsClass
{
}
[OmsName("Driver"), OmsGlobalIdentifier("{10c51c07-c757-4b00-b406-d54c99460f32}")]
@ -43,13 +60,20 @@ public class VehicleForHireDB : OmsDatabase
[OmsName("Vehicle Permit"), OmsGlobalIdentifier("{38a492b9-901f-4074-af45-642e812b51f5}")]
public class VehiclePermit : OmsClass
{
[OmsName("Permit Index"), OmsGlobalIdentifier("{759f4beb-ee43-47b8-9add-16967bba8ec8}")]
public int PermitIndex { get { return (int) GetAttributeValue(); } set { SetAttributeValue(value); } }
[OmsName("Permit Name"), OmsGlobalIdentifier("{c4f4a59f-c246-4d88-a2fd-588195d5e943}")]
public string PermitName { get; set; }
}
public readonly IList<Business> Businesses;
}
public class VehicleForHireTests : OmsModelingTestsBase
{
protected override OmsDatabase GetDatabase() => new VehicleForHireDB();
private OmsDatabase db = new VehicleForHireDB();
protected override OmsDatabase GetDatabase() => db;
[Test]
public void ClassesCreated()
@ -61,8 +85,12 @@ public class VehicleForHireTests : OmsModelingTestsBase
}
[Test]
public void RelationshipsCreated()
{
public void RelationshipsCreated() {
CheckRelationshipExists(new Guid("{867126b9-294e-454f-b9c5-88df44353014}"), new Guid("{4b6f186c-babe-4c42-840b-3840373f68c2}"), "has", new Guid("{38a492b9-901f-4074-af45-642e812b51f5}"));
VehicleForHireDB db = (VehicleForHireDB) GetDatabase();
VehicleForHireDB.Business busn = new VehicleForHireDB.Business("Ryde Rentals Transportation");
db.Businesses.Add(busn);
}
}