Modelling hierarchy with bit column as discriminator

Posts   
 
    
Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 04-Sep-2007 14:38:32   

Hi,

Correct me if I'm wrong but it seems I cannot do what the title of this post says with LLBLGen (documentation also doesn't mention bool as a valid discriminating type). My question is why this scenario is not supported?

Also when playing with the inheritance support I have noticed that even though I mark a base entity as abstract it still needs a discriminator value - obviously I'm never going to use the abstract class directly so I don't see the point of assigning a discriminator value to it as it kind of makes the model confusing.

Also it seems to me that this requirement (assigning discriminator value to base abstract type) somehow eliminates the use of bool as discriminating type (only two values so only one sub type can be created which defeats the purpose of inheritance).

Best regards, Robert.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 05-Sep-2007 11:05:33   

Correct me if I'm wrong but it seems I cannot do what the title of this post says with LLBLGen (documentation also doesn't mention bool as a valid discriminating type). My question is why this scenario is not supported?

IMHO, Inheritace Hierarchies should be expandable, i.e. you can define a new subType later on. Using a bit/boolean column will defeat that concept.

Also when playing with the inheritance support I have noticed that even though I mark a base entity as abstract it still needs a discriminator value - obviously I'm never going to use the abstract class directly so I don't see the point of assigning a discriminator value to it as it kind of makes the model confusing.

I see your point, but I think the following lines from the docs might be the answer to your question:

An abstract entity means that you can have instances of that entity in multi-entity polymorphic fetches but you can't instantiate an entity instance in code by using its constructor.

So there can already be some rows in the database that belongs to that type.

Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 07-Sep-2007 15:31:00   

Walaa wrote:

IMHO, Inheritace Hierarchies should be expandable, i.e. you can define a new subType later on. Using a bit/boolean column will defeat that concept.

Still you have to redeploy the application to extend the hierarchy so when such change is required the type of the column could be changed simple_smile Nevertheless you have a point there.

I see your point, but I think the following lines from the docs might be the answer to your question:

An abstract entity means that you can have instances of that entity in multi-entity polymorphic fetches but you can't instantiate an entity instance in code by using its constructor.

So there can already be some rows in the database that belongs to that type.

I will probably never have abstract class instances stored in the database (just doesn't feel right), but now that I know that the reason for the limitation is actually more flexibility it will be easier to accept not using bit column as discriminator. Thanks for you help Walaa.

Robert.

Posts: 3
Joined: 13-Jan-2020
# Posted on: 14-Jan-2020 13:28:12   

Same problem. Solved (SelfServing) with: a) A BooleanToInteger Type Converter b) A small hacking on EntityFactories.cs (case switch). It is required because Type Converter is applied after EntityFactory call.

public override IEntityFactory GetEntityFactory(object[] fieldValues, Dictionary<string, int> entityFieldStartIndexesPerEntity)
{
    int bGrupoIndex = (int)OpEDirectorioFieldIndex.BGrupo;
    switch (ForEntityName)
    {
        case "OpEDirectorioEntity":
        case "DirectorioSubgruposEntity":
        case "DirectorioUsuariosEntity":
            int bGroup = (bool)fieldValues[bGrupoIndex] ? 1 : 0;
            fieldValues[bGrupoIndex] = bGroup;
            break;
    }
    return (IEntityFactory)ModelInfoProviderSingleton.GetInstance().GetEntityFactory(ForEntityName, fieldValues, entityFieldStartIndexesPerEntity) ?? this;
}

Note that, strictly, the Type Converter is only required for the designer, as the code really converts from int (hack) to int. I send the type converter code anyway


using System;
using System.ComponentModel;

namespace SD.LLBLGen.Pro.TypeConverters
{
    /// <summary>
    /// Implementation of a Int32ToBooleanConverter. This converter converts bit database type to integer (Int32).
    /// Any value other than 0 is seen as true, and 0 is seen as false.
    /// Used for Boolean as inheritance discriminator field. See https://llblgen.com/TinyForum/NewMessage.aspx?ThreadID=11150
    /// </summary>
    /// <remarks>This implementation is targeted to be used as a converter which is instantiated through code, not through TypeDescriptor.GetConverter.
    /// This means that this converter can be used in an attribute on a type, but the implementation can fail (but doesn't have to) in that 
    /// situation as it's not tested in that scenario. The core difference is that 'context' is ignored in the converter code.</remarks>
    [Description("Converter with as core type System.Int32, for mapping a field with a .NET type System.Int32 onto a bit database field")]
    public class Int32ToBooleanConverter : TypeConverter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Int32ToBooleanConverter"/> class.
        /// </summary>
        public Int32ToBooleanConverter()
        {
        }

        /// <summary>
        /// Returns whether this converter can convert an object of the given type to the type of this converter (Int32).
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="sourceType">A <see cref="T:System.Type"/> that represents the type you want to convert from.</param>
        /// <returns>
        ///  <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
        /// </returns>
        /// <remarks>Accepted types are: Boolean</remarks>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            // any integer type is accepted. No fractional types like float/double.
            switch (sourceType.FullName)
            {
                case "System.Boolean":
                case "System.Int32":
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Returns whether this converter can convert the object to the specified type.
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="destinationType">A <see cref="T:System.Type"/> that represents the type you want to convert to.</param>
        /// <returns>
        ///  <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
        /// </returns>
        /// <remarks>Accepted types are: Boolean</remarks>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            // any integer type is accepted. No fractional types like float/double.
            switch (destinationType.FullName)
            {
                case "System.Int32":
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Converts the given object to the type of this converter (Int32).
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="culture">Ignored</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value, which is of type Int32.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The conversion could not be performed.</exception>
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            int toReturn;
            switch (value.GetType().FullName)
            {
                case "System.Boolean":
                    toReturn = ((bool)value) ? 1 : 0;
                    break;
                case "System.Int32":
                    toReturn = ((int)value > 0) ? 1 : 0;
                    break;
                default:
                    throw new NotSupportedException("Conversion from a value of type '" + value.GetType().ToString() + "' to System.Int32 isn't supported");
            }

            return toReturn;
        }

        /// <summary>
        /// Converts the given value object to the specified type
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="culture">Ignored</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <param name="destinationType">The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value. The value will be True if <paramref name="value"/> is > 0, otherwise False
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="destinationType"/> parameter is <see langword="null"/>.</exception>
        /// <exception cref="T:System.NotSupportedException">The conversion could not be performed.</exception>
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value", "Value can't be null");
            }

            if (!(value is int))
            {
                throw new ArgumentException("Value isn't of type integer", "value");
            }

            bool toReturn = ((int)value > 0);
            return toReturn;
        }


        /// <summary>
        /// Creates an instance of the Type that this <see cref="T:System.ComponentModel.TypeConverter"/> is associated with (Int32)
        /// </summary>
        /// <param name="context">ignored.</param>
        /// <param name="propertyValues">ignored.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> of type Int32. It always returns '1' for this converter.
        /// </returns>
        public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
        {
            int objeto = 1;
            return objeto;
        }
    }
}