Spring Nullable Type: TValue.FromVariant is still plaguing us.
We are using Nullable<T> and just taking the Value from our ORACLE Result set and setting it into a class. that uses Nullable<TDateTime> Like So:
Uses Spring;
type
TCustomerHistoryStatistic = class
public
property LastEditDate : Nullable<TDateTime> . . . .
end;
Procedure LoadStatistics
var
query: TQueryWrapper;
begin
query.SetSQLText(tmpQuery);
query.Open;
while not query.EOF do
begin
Resource := TCustomerHistoryStatistic.Create;
Resource.LastEditDate := query.FieldByName(csfModifiedDatetime).Value;
end;
This code runs fine but when we attempt to get the value it blows with an invalid variant type because of the Oracle types that are not supported by TValue.FromVariant
The following code in the Spring unit fails
class operator Nullable<T>.Implicit(const value: Variant): Nullable<T>;
var
v: TValue;
begin
if not VarIsNullOrEmpty(value) then
begin
v := TValue.FromVariant(value);
Result := Nullable<T>.Create(v.AsType<T>);
end
else
Result.Clear;
end;
Once again I would like to suggest that the FromVariant Utility method in Spring.Persistence.Core.Utils.TUtils should be used everywhere TValue.FromVariant is called.
Comments (8)
-
repo owner -
reporter No because if the Field coming from the database is null then asDateTime fails. Whats the use of a nullable type then if you have to check for null on the value you are putting into it.
I agree that it should not be Spring4d's responsibility to handle these variant types. In my opinion it is a shortfall of TValue.FromVariant method, but since Embarcadero is unlikely to fix it any time soon, somehow it will need to be handled. Since the persistence layer already handles it and the Nullable<T> type will likely mostly be used for persistence I just thought that you might want to reconsider providing a solution. Especially since it seems to be a simple solution.
Plus I don't believe the BCDVariant and SqlTimeStampVariant types are necessarily Oracle specific, we are just seeing them with Oracle. Maybe other databases use them as well. When Marshmallow is released and both it and Nullable type are more widely used by others it is my guess you will be seeing this issue more often reported by others.
BTW I have looked in XE7 and the fromVariant method has not changed. I will report this issue to Embarcadero as well and see how they respond. Thanks for your time and consideration.
--
-
repo owner So we are talking only about these?
http://docwiki.embarcadero.com/Libraries/XE7/en/Data.FmtBcd.TBcd http://docwiki.embarcadero.com/Libraries/XE7/en/Data.FmtBcd.VarFMTBcdCreate
http://docwiki.embarcadero.com/Libraries/XE7/en/Data.SqlTimSt.TSQLTimeStamp http://docwiki.embarcadero.com/Libraries/XE7/en/Data.SqlTimSt.VarSQLTimeStampCreate
-
reporter Yes And one more
http://docwiki.embarcadero.com/Libraries/XE7/en/Data.SqlTimSt.TSQLTimeStampOffset http://docwiki.embarcadero.com/Libraries/XE7/en/Data.SqlTimSt.TSQLTimeStampOffsetCreate
Also I get what you are saying about Variant types and trying to handle all sorts of special types. These types are part of the VCL and should be handled by the TValue.FromVariant method. I would guess that it is an oversight on Embarcadero's part. I have reported this as Embarcadero Bug #130614. We will see what they say.
-
repo owner New bugs should go into the new system (quality.embarcadero.com) - those in the old one are more likely to be ignored. Also it's RTL and not VCL.
-
repo owner I looked a bit into it and handling this in TValue.FromVariant is not that easy. Since the VType for custom variant type is dynamic you cannot assume that its a fixed number (but you know that from your research of the problem). Also making Spring.Base depend on any unit from dbrtl is not an option.
So I guess that leaves us with two choices:
1) hardcoding it the way you solved this in TUtils.FromVariant and being limited to what is being hardcoded there
- pro: simple enough
- con: hardcoded and requires changes to Spring.pas for supporting new custom variant types
2) providing some way of registering converters that handle custom variant to TValue
- pro: flexible
- con: more complicated and you need the correct spot to register them
While I would like to make it flexible enough I guess the first step would be to hardcode the handling of the mentioned three types.
-
repo owner I added TValueHelper.FromVariant some while ago which should solve that problem as it is used in the implicit operator of Nullable<T>. Please check if now your code runs properly.
-
repo owner - changed status to closed
- Log in to comment
And why should Nullable<T> know about some special treatment that relates to Oracle? Variants are variants and they can hold all sorts of stuff. That does not mean that everything that knows about Variant and puts them into TValue should try to know how to do that - especially since it just can't.
Just don't use the implicit overload for Variant in that case and write:
Does that solve the problem?
Or just write some conversion function that turns that oracle thing into a regular Variant (as in one that TValue understands) and use that in your code.
The reason why I am trying to avoid this is that it will lead to handling all sorts of custom variant types when converting to TValue and these Oracle types would be just the beginning. What's next? I don't know how many different custom variant types are out there that could/should be handled in such conversion routine.
How do these values convert to delphi data types anyway? Does a direct assignment to TDataTime or other types work?