Issue with DataMapper.Map() for enums

Issue with DataMapper.Map() for enums

Old forum URL: forums.lhotka.net/forums/t/4351.aspx


rd_bigdog posted on Thursday, February 14, 2008

Hi Group,

Wondering if anyone came accross this?

My BO exposes a series of properties as ENUM types. My DTO defines the same fields as integers. The datamapper works when going from BO->DTO. But when going from DTO->BO, it fails on the conversion.

Going under the hood, I see that the CoerseValue does check for ENUM types, however, it also ensure that the type is a string:

 If desiredType.IsEnum AndAlso valueType.Equals(GetType(String)) Then
        Return System.Enum.Parse(desiredType, value.ToString())
 End If

This didn't make sense to me as Enums cannot be declared as string that I am aware of. Then, if the type was a string, why would you need to call ToString() on it?

I removed the check for string and recompiled the assembly and all is good. I just want to make sure that I am understanding the problem and not missing something more significant?

Thanks,

Rick.

RockfordLhotka replied on Thursday, February 14, 2008

The value parameter is of type object, which is why I did ToString().

So you removed both the type check and the ToString() and it works with both integer and string input types?

rd_bigdog replied on Friday, February 15, 2008

Hi Rocky,

I did not test with String because I am unaware of how to create a an enum as a string,

IE. Public enum as string

     myenumone = "test"

end enum

I think for that second part of the condition to ever past you would have to be able to create an enum as a string. So I am not sure if the concept of string here is relevant? If it is an enum type, it will be some numeric type and not a string. So the valueType.Equals(GetType(String))  check will always be FALSE and the mapping will always throw an exception when going from an integer to an enum.

I left the ToString() in as it should be a numeric type and as you indicated it is Object, so it is cleaner to have the ToString(), I only took out the check in the if condition for String type.

True?

Also, I ever mentioned what version I am using, this is the latest beta version of the CSLA 3.5...not sure if this issue exists in previous versions.

Rick.

RockfordLhotka replied on Friday, February 15, 2008

The purpose of the code you are looking at is specifically to handle the case of mapping a string value into an enum. So if you have an enum that defines values like Red=0 and Blue=1 then you can pass in a string value like “Blue” and have it automatically end up as MyEnum.Blue, or 1.

 

Many people use comboboxes or other string representations of enum values, and the point of this code is to make it easier to simply copy the string value from the UI (or data file or whatever) directly into an enum property of your business object.

 

I hadn’t thought about trying to use this to auto-parse integer values into an enum, and I’m not sure it is necessary, because an integer value should be convertible to an enum without all the overhead of calling the parse method. This is why that particular path only occurs when the value is a  string – that’s the only type of value you’d want to parse into an enum. Numeric types should directly convert.

 

Or do they not convert? And if they don’t, what is the exception?

 

Rocky

 

From: rd_bigdog [mailto:cslanet@lhotka.net]
Sent: Friday, February 15, 2008 9:28 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Issue with DataMapper.Map() for enums

 

Hi Rocky,

I did not test with String because I am unaware of how to create a an enum as a string,

IE. Public enum as string

     myenumone = "test"

end enum

I think for that second part of the condition to ever past you would have to be able to create an enum as a string. So I am not sure if the concept of string here is relevant? If it is an enum type, it will be some numeric type and not a string. So the valueType.Equals(GetType(String))  check will always be FALSE and the mapping will always throw an exception when going from an integer to an enum.

True?

Also, I ever mentioned what version I am using, this is the latest beta version of the CSLA 3.5...not sure if this issue exists in previous versions.

Rick.



rd_bigdog replied on Friday, February 15, 2008

Ok, thanks for the explanation. I knew that had to be more to it as you wouldn't have code in there that didn't ever server a purpose...I didn't see that angle.

I agree 100%, I have never seen a problem directly converting an integer to an enum and vice versa, it should just work.

This is the exception (I left in the pertinent parts) (and the code from a small sample is below that generated the exception) :

System.ArgumentException was unhandled
  Message="Property copy failed (MyEnum)"
  Source="Csla"
  StackTrace:
    .......
  InnerException: System.InvalidCastException
       Message="Invalid cast from 'System.Int32' to 'enumTest.testENum'."
       Source="mscorlib"
       StackTrace:
            at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)    at System.Int32.System.IConvertible.ToType(Type type, IFormatProvider provider)    at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)    at System.Convert.ChangeType(Object value, Type conversionType)    at Csla.Utilities.CoerceValue(Type desiredType, Type valueType, Object oldValue, Object value) in D:\Working\AMS\AMS Development\AMS Rearchitecture\AMSCSLA\AMSCSLA\Csla\Utilities.vb:line 143    at Csla.Data.DataMapper.SetValue(Object target, MemberInfo memberInfo, Object value) in D:\Working\AMS\AMS Development\AMS Rearchitecture\AMSCSLA\AMSCSLA\Csla\Data\DataMapper.vb:line 321    at Csla.Data.DataMapper.SetPropertyValue(Object target, String propertyName, Object value) in D:\Working\AMS\AMS Development\AMS Rearchitecture\AMSCSLA\AMSCSLA\Csla\Data\DataMapper.vb:line 285    at Csla.Data.DataMapper.Map(Object source, Object target, Boolean suppressExceptions, String[] ignoreList) in D:\Working\AMS\AMS Development\AMS Rearchitecture\AMSCSLA\AMSCSLA\Csla\Data\DataMapper.vb:line 184
       InnerException:

 

This code will execute fine down to the last test where we are convertign the DTO to the business object (as defined by my application where my BO has teh Enum properties and my DTO has the integer properties), so inheritenly, the Integer to the Enum. Just copy into a winform with a button and click.

 

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Test BO -> DTO (Enum to Integer)
        Dim theBO As myBO = New myBO
        theBO.MyEnum = testENum.four
        Dim theDTO As myDTO = New myDTO
        Csla.Data.DataMapper.Map(theBO, theDTO)

'Raw implicit conversions without DataMapper.Map       

Dim theENum As testENum = testENum.three
        Dim theInt As Integer
        theInt = theENum
        theENum = theInt

        'Test DTO -> BO (Integer to the Enum)
        Dim theBO2 As myBO = New myBO
        Dim theDTO2 As myDTO = New myDTO
        theDTO2.MyEnum = 4
        Csla.Data.DataMapper.Map(theDTO2, theBO2)

    End Sub
End Class

Public Enum testENum
    one
    two
    three
    four
End Enum


Public Class myDTO

    Private _testEnum As Integer

    Public Property MyEnum() As Integer
        Get
            Return _testEnum
        End Get
        Set(ByVal value As Integer)
            _testEnum = value
        End Set
    End Property
End Class

Public Class myBO

    Private _testEnum As testENum

    Public Property MyEnum() As testENum
        Get
            Return _testEnum
        End Get
        Set(ByVal value As testENum)
            _testEnum = value
        End Set
    End Property
End Class

amselem replied on Saturday, February 16, 2008

Try to declare the enum with the integers explicitly:

Public Enum testENum
    one  = 1
    two = 2
    three = 3
    four = 4
End Enum

If you don't write them explicitly, they get numerated automatically but starting from zero (so one = 0, two = 1, three = 2 and four = 3)

Regards

RockfordLhotka replied on Saturday, February 16, 2008

Interesting. I’ll add a unit test for this and see about fixing the issue. I would have expected that scenario to simply work though, so apparently the basic Convert call I’m making isn’t quite as good as a normal cast :(

 

Rocky

RockfordLhotka replied on Saturday, February 16, 2008

I added code so it will now attempt to coerce both inbound string values, and values that match the underlying type of the enum (typically int), this is now in svn.

 

Thank you for your help in finding and researching this issue!

 

Rocky

rd_bigdog replied on Saturday, February 16, 2008

No problem, Thanks for the quick turnaround on it!

That's great!

Rick.

Copyright (c) Marimer LLC