Reputation: 57
I have an object
type variable where I want to cast it to its original data type. Data type could be anything (int, enum, class, etc)
(object.GetType())object
is not working
Edited:
The reason I want to cast it again to original, because I'm building a cosmos db query with multiple parameters
I have a method QueryCosmos(Dictionary<string, object> parameters)
where key is the field name in cosmos, and value is the value of the field that matches the condition (it could be any data type).
static string QueryCosmos(Dictionary<string, object> parameters)
{
var sb = new StringBuilder($"SELECT * FROM c WHERE ");
foreach (var p in parameters)
{
if (!(p.Value is string))
{
// this handles non-string data types
var value = p.Value == null ? "null" : p.Value.ToString().ToLower();
sb.Append($"c.{p.Key} = {value} and ");
}
else
{
sb.Append($"c.{p.Key} = '{p.Value}' and ");
}
}
return sb.ToString().Substring(0, sb.ToString().LastIndexOf(" and "));
}
My issue is with this line of code var value = p.Value == null ? "null" : p.Value.ToString().ToLower();
If I pass on an enum
type, doesn't match the data in cosmos. Because in cosmos, this is stored as the numeric value of the enum. I would need it to convert to its numeric
value. There are multiple possible enum
types that can be passed. That's the reason why I wanted a dynamic way to covert to original data type
Hope this clears up the concern
Upvotes: 0
Views: 1356
Reputation: 9209
I've not looked at your new edit, but hopefully my answer is still relevant.
If you wish to cast an instance to its known concrete class type then you could use the as
operator.
Here's some example code:
using System.IO;
using System;
using System.Collections.Generic;
class Program
{
public class BaseClass{
public string Base="BaseClass";
}
public class Class1:BaseClass{
public string Value="Class 1";
}
public class Class2:BaseClass{
public string Value="Class 2";
public string AnotherValue="Another value";
}
static void Main()
{
List<BaseClass> list=new List<BaseClass>(){new Class1(), new Class2()};
foreach( var item in list)
{
Console.WriteLine($"Item.Base = {item.Base}");
if(typeof(Class1).IsAssignableFrom( item.GetType() ))
{
Console.WriteLine($"Item.Value = {(item as Class1).Value}");
}
if(typeof(Class2).IsAssignableFrom( item.GetType() ))
{
Console.WriteLine($"Item.Value = {(item as Class2).Value}");
Console.WriteLine($"Item.Value = {(item as Class2).AnotherValue}");
}
}
}
}
This will give you the output of:
Item.Base = BaseClass
Item.Value = Class 1
Item.Base = BaseClass
Item.Value = Class 2
Item.Value = Another value
Upvotes: 1
Reputation: 155708
As you're targeting Azure CosmosDB, you don't need to write your own query-generator, the CosmosDB client library (Microsoft.Azure.Cosmos
) has one built-in.
In your case, use QueryDefinition
and WithParameter
to build a strongly-typed query. The WithParameter
method accepts values as object
and it handles type-specific logic for you.
As you are using a dynamic-query my code below shows how you can safely build a query's WHERE
clause from a dictionary by using named-parameters.
Never embed query parameter values directly in the query SQL itself because doing so opens yourself up to SQL injection which is a very bad thing.
enum
:I'll admit that I'm unfamiliar with the Azure CosmosDB client library, though I did take a quick look at the documentation and poked around the library in ILSpy.
As for handling enum
values: the QueryDefinition.WithParameter
method should handle enum
values correctly as it preserves the boxed enum values in its .Value
property.
If it doesn't, then converting any enum
to an Int32
can be done like so - albeit with boxing (while it is possible to cast any enum to Int64
without casting that requires runtime IL emit which is beyond the scope of this answer, but I have an implementation in my GitHub Gists if you're interested):
I've updated the code below to use this foreach
loop which checks for Enum
(which matches any and all enum
types, regardless of their underlying-type, though this example will fail if you have any enum : Int64
enums with values outside Int32
's range - but that's trivial to fix and so is left as an exercise for the reader).
foreach( var kvp in parameters )
{
if( kvp.Value is Enum e )
{
Int32 enumAsInt32 = (Int32)Convert.ChangeType( e, TypeCode.Int32 );
query = query.WithParameter( kvp.Key, enumAsInt32 );
}
else
{
query = query.WithParameter( kvp.Key, kvp.Value );
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
//
public static QueryDefinition BuildCosmosQuery( Dictionary<String,Object> parameters )
{
String sqlText = "SELECT * FROM foobar AS f WHERE " + GetWhereClauses( "f", parameters.Keys );
QueryDefinition query = new QueryDefinition( sqlText );
foreach( var kvp in parameters )
{
if( kvp.Value is Enum e )
{
Int32 enumAsInt32 = (Int32)Convert.ChangeType( e, TypeCode.Int32 );
query = query.WithParameter( kvp.Key, enumAsInt32 );
}
else
{
query = query.WithParameter( kvp.Key, kvp.Value );
}
}
return query;
}
private static String GetWhereClauses( String tableAlias, IEnumerable<String> parameterNames )
{
return parameterNames
.Select( pn => "{0}.{1} = @{1}".FmtInv( tableAlias, pn ) )
.StringJoin( separator: " AND " );
}
//
public static class Extensions
{
public static String StringJoin( this IEnumerable<String> source, String separator )
{
return String.Join( values: source, separator: separator );
}
public static String FmtInv( this String format, params Object[] args )
{
return String.Format( CultureInfo.InvariantCulture, format, args: args );
}
}
See the QueryWithSqlParameters
method in this code sample to see how to use QueryDefinition
.
Upvotes: 2