How to get type discriminator for class?

1k Views Asked by At

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?

2

There are 2 best solutions below

1
Kjell Rilbe On BEST ANSWER

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).

10
Hans Karlsen On

The discriminator 72 is also the index in the TypeService list of Classes.

IClass umlClass = (IClass)TypeSystem.AllClasses[72];