Search code examples
c#mdrivenmodel-driven-development

How to get type discriminator for class?


In MDriven I'd like to get the type discriminator for a modeled class at runtime. I thought I had done it before, but can't seem to find it and can't figure it out.

I mean the integer that appears first in the external id string, e.g. "72" in external id "72!i64!12345678".

I need to get the discriminator based on C# type, i.e. typeof(MyClass), without having any object instance available. This makes it impossible to extract the discriminator from the external id string, because I have no such string available at the time and can't create one.

I'm using Eco.Services.Impl.ExternalIdServiceImpl_DbType so the external id's type discriminator matches the type discriminator in the database, in case that matters. But how do I lookup the discriminator for a type, in runtime?


Solution

  • After finding source code for Eco.Services.Impl.ExternalIdServiceImpl_DbType I was able to create this solution:

    public static string GetClassDiscriminator<T>(this IEcoServiceProvider sp)
    {
        IEcoTypeSystem typeSys = sp.GetEcoService<ITypeSystemService>().TypeSystem;
        IClass cls = typeSys.AllClasses.Cast<IClass>().First(c => c.ObjectType == typeof(T));
        if (sp.GetEcoService<IExternalIdService>() is Eco.Services.Impl.ExternalIdServiceImpl_DbType)
        {
            ORMappingDefinition mapping = ((PersistenceMapperDb)DiamondsPMP.Instance.PersistenceMapper).EffectiveRunTimeMappingProvider.Mapping;
            if (mapping == null)
                throw new InvalidOperationException(PersistenceStringRes.MappingProviderNotInitialized);
            ClassDefinition classdef = mapping.Classes[typeSys.AllClasses[0].Name];
            if (classdef.Discriminators.Count == 0)
                throw new InvalidOperationException(PersistenceStringRes.RootclassHasNoDiscriminatorDefined);
            DiscriminatorDef discriminator = classdef.Discriminators.Cast<DiscriminatorDef>().First();
            DiscriminatorValue discvalue = discriminator.DiscriminatorValuesByClassId(cls.InternalIndex);
            if (discvalue == null)
                throw new InvalidOperationException(PersistenceStringRes.ClassHasNoDiscriminatorValueDefined);
            if (discvalue.IsFinal && cls.SubTypes.Count > 0)
                throw new InvalidOperationException(PersistenceStringRes.DiscriminatorIsFinal);
            return discvalue.Value;
        }
        else
            return cls.InternalIndex.ToString();
    }
    

    The sp.GetEcoService<IExternalIdService>() is Eco.Services.Impl.ExternalIdServiceImpl_DbType is required in my case, because in my unit tests i use PersistenceMapperMemory, which uses the index in TypeSystem.AllClasses as type discriminator, which is also the value returned by IClass.InternalIndex.

    I also note that IExternalIdService.ObjectForIdSeperateClassInfo() is buggy. It acts as if Eco.Services.Impl.ExternalIdServiceImpl is being used, returning an IObjectInstance of the wrong type (using the class' index in TypeSystem.AllClasses).