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.
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?
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.
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.
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
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
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
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
No problem, Thanks for the quick turnaround on it!
That's great!
Rick.
Copyright (c) Marimer LLC