RepoDB: Enums: error when dbValue doesn't exist in Enum-Values

Bug Description

I think #956 has the same problem.

If I have values in table, which doesn’t exist in the given Enum, then I get the following error. I don’t use string-values. I use byte, short, int, long.

Exception Message:

System.ArgumentNullException: 'Value cannot be null. Arg_ParamName_Name'

Model:

    public enum TestEnum : int
    {
        None = 0,
        Type1 = 1
    }
    public class TestTable
    {
        public long ID { get; set; }
        public TestEnum TestType { get; set; }
    }

Compiler creates:

new TestTable() 
{
    ID = reader.GetInt64(0),
    TestEnum = Convert(Parse(TestEnum, GetName(TestEnum, Convert(reader.GetInt32(1), Object)), True), TestEnum)
}

Assume reader.GetInt32(1) returns "4". But "4" doesn't exist in TestEnum. Then GetName returns "null". Parse throws the error...

Expected Behaviour return default(TestEnum)

Possible Solution

long dbValue = 4; //note, in this case the db-type differs from the corresponding enum-type int
var baseType = typeof(TestEnum).GetEnumUnderlyingType();

object convertedDbValue = dbValue;
if (baseType != dbValue.GetType()) //if dbValue-Type differs from underlying enum-type
	convertedDbValue = Convert.ChangeType(dbValue, baseType); //ex. long to int

object result;
if (Enum.IsDefined(typeof(TestEnum), convertedDbValue))
	result = Enum.ToObject(typeof(TestEnum), dbValue);
else
	result = default(TestEnum);
return result;

Solution translated to Compiler Replace the following Method in Compiler.cs:

(I never used Expression before!)

internal static Expression ConvertExpressionToEnumExpressionForNonString(Expression expression, Type toEnumType)
{
	Type baseType = toEnumType.GetEnumUnderlyingType();
	if (baseType != expression.Type)
	{
		var convertMethod = StaticType.Convert.GetMethod("ChangeType", new[] { StaticType.Object, StaticType.Type });
		var convertParameters = new Expression[]
		{
			Expression.Convert(expression, StaticType.Object),
			Expression.Constant(baseType)
		};
		expression = Expression.Call(convertMethod, convertParameters);
	}
	else
		expression = Expression.Convert(expression, StaticType.Object);

	var isDefinedMethod = StaticType.Enum.GetMethod("IsDefined", new[] { StaticType.Type, StaticType.Object });
	var isDefinedParameters = new Expression[]
	{
			Expression.Constant(toEnumType),
			expression
	};            
	var isDefinedExpression = Expression.Call(isDefinedMethod, isDefinedParameters);

	var toObjectMethod = StaticType.Enum.GetMethod("ToObject", new[] { StaticType.Type, StaticType.Object });
	var toObjectParameters = new Expression[]
	{
			Expression.Constant(toEnumType),
			expression
	};
	var toObjectExpression = Expression.Call(toObjectMethod, toObjectParameters);

	var defaultExpression = Expression.Convert(Expression.Default(toEnumType), StaticType.Object);

	return Expression.Condition(isDefinedExpression, toObjectExpression, defaultExpression);
}

Library Version: Current master.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Oh. Looks like then, we might have built the package from the master before the actual fixes/code has been merged then. Anyway, we can issue a new alpha, or maybe promote to a beta so we can start work on our documentations. We will notify you ASAP once the new package has been pushed.

Using GlobalConfiguration.Options.EnumHandling

public enum EnumHandling
{
    ThrowError = 0, //Default
    UseDefault = 1,
    Cast = 2
}

internal static Expression ConvertExpressionToEnumExpressionForNonString(Expression expression, Type toEnumType)
{
    EnumHandling handler = EnumHandling.ThrowError; //GlobalConfiguration.Options.EnumHandling

    if (handler == EnumHandling.Cast)
        return Expression.Convert(expression, toEnumType);



    Type baseType = toEnumType.GetEnumUnderlyingType();
    if (baseType != expression.Type)
    {
        var convertMethod = StaticType.Convert.GetMethod("ChangeType", new[] { StaticType.Object, StaticType.Type });
        var convertParameters = new Expression[]
        {
            Expression.Convert(expression, StaticType.Object),
            Expression.Constant(baseType)
        };
        expression = Expression.Call(convertMethod, convertParameters);
    }
    else
        expression = Expression.Convert(expression, StaticType.Object);

    var isDefinedMethod = StaticType.Enum.GetMethod("IsDefined", new[] { StaticType.Type, StaticType.Object });
    var isDefinedParameters = new Expression[]
    {
            Expression.Constant(toEnumType),
            expression
    };
    var isDefinedExpression = Expression.Call(isDefinedMethod, isDefinedParameters);


    Expression notDefinedExpression;
    if (handler == EnumHandling.UseDefault)
        notDefinedExpression = Expression.Convert(Expression.Default(toEnumType), StaticType.Object);
    else
    {
        var stringConcat = StaticType.String.GetMethod("Concat", new[] { typeof(string), typeof(string) });
        var errorMessage = Expression.Call(stringConcat, Expression.Constant(toEnumType.Name + " doesn't define enum-value "), Expression.Call(expression, StaticType.Object.GetMethod("ToString")));

        var argumentExceptionConstructor = typeof(ArgumentException).GetConstructor(new[] { StaticType.String });
        var throwException = Expression.Throw(Expression.New(argumentExceptionConstructor, errorMessage), StaticType.Object);

        notDefinedExpression = throwException;
    }


    var toObjectMethod = StaticType.Enum.GetMethod("ToObject", new[] { StaticType.Type, StaticType.Object });
    var toObjectParameters = new Expression[]
    {
            Expression.Constant(toEnumType),
            expression
    };
    var toObjectExpression = Expression.Call(toObjectMethod, toObjectParameters);

    return Expression.Condition(isDefinedExpression, toObjectExpression, notDefinedExpression);
}