0

我有一个object类型变量,我想将其转换为其原始数据类型。数据类型可以是任何东西(int、enum、class 等)

(object.GetType())object不管用

编辑:

我想再次将其转换为原始的原因,因为我正在构建一个带有多个参数的 cosmos db 查询

我有一个方法QueryCosmos(Dictionary<string, object> parameters),其中 key 是 cosmos 中的字段名称,value 是与条件匹配的字段的值(它可以是任何数据类型)。

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 "));
}

我的问题是这行代码var value = p.Value == null ? "null" : p.Value.ToString().ToLower();

如果我传递一个enum类型,与 cosmos 中的数据不匹配。因为在cosmos中,这是存储为枚举的数值。我需要它转换成它的numeric价值。enum可以传递多种可能的类型。这就是为什么我想要一种动态方式来转换为原始数据类型的原因

希望这可以消除担忧

4

2 回答 2

2

由于您以 Azure CosmosDB 为目标,因此无需编写自己的查询生成器,CosmosDB 客户端库 ( Microsoft.Azure.Cosmos) 有一个内置的.

在您的情况下,使用QueryDefinitionandWithParameter来构建强类型查询。该WithParameter方法接受值 asobject并为您处理特定于类型的逻辑。

当您使用动态查询时,下面的代码显示了如何使用命名参数从字典中安全地构建查询子句。WHERE

永远不要将查询参数值直接嵌入查询 SQL 本身,因为这样做会导致 SQL 注入,这是一件非常糟糕的事情


更新enum

我承认我不熟悉 Azure CosmosDB 客户端库,尽管我确实快速查看了文档并在 ILSpy 中浏览了该库。

至于处理enum值:该QueryDefinition.WithParameter方法应该enum正确处理值,因为它在其.Value属性中保留了装箱的枚举值。

如果不是,则可以像这样将 any 转换enum为 an - 尽管使用装箱(虽然可以将任何枚举强制转换为不需要运行时 IL 发射的强制转换,这超出了此答案的范围,但我有一个如果您有兴趣,请在我的 GitHub Gists 中实现):Int32Int64

我已经更新了下面的代码以使用这个foreach循环来检查Enum(它匹配任何所有 enum类型,不管它们的底层类型如何,尽管如果你有任何enum : Int64值超出Int32's 范围的枚举,这个例子将会失败——但这对修复,所以留给读者练习)。

    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 );
    }
}

请参阅QueryWithSqlParameters此代码示例中的方法以了解如何使用QueryDefinition.

于 2021-09-08T08:37:03.507 回答
1

我没有看过你的新编辑,但希望我的回答仍然相关。

如果您希望将实例转换为其已知的具体类类型,则可以使用as运算符。

这是一些示例代码:

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}");
            }
        }
    }
}

这将为您提供以下输出:

Item.Base = BaseClass
Item.Value = Class 1
Item.Base = BaseClass
Item.Value = Class 2
Item.Value = Another value
于 2021-09-08T08:16:08.037 回答