【问题标题】:Using Dynamic LINQ (or Generics) to query/filter Azure tables使用动态 LINQ(或泛型)查询/过滤 Azure 表
【发布时间】:2010-09-16 22:51:14
【问题描述】:

所以这是我的困境。我正在尝试利用动态 LINQ 解析搜索过滤器,以从 Azure 表中检索一组记录。目前,我可以使用如下定义的 GenericEntity 对象获取所有记录:

public class GenericEntity
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }

    Dictionary<string, object> properties = new Dictionary<string, object>();
    /* "Property" property and indexer property omitted here */
}

我可以通过利用 TableServiceContext 对象的 ReadingEntity 事件(称为 OnReadingGenericEvent)来完全填充它。以下代码实际上是提取所有记录并希望过滤(一旦我开始工作)。

public IEnumerable<T> GetTableRecords(string tableName, int numRecords, string filter)
{
    ServiceContext.IgnoreMissingProperties = true;

    ServiceContext.ReadingEntity -= LogType.GenericEntity.OnReadingGenericEntity;
    ServiceContext.ReadingEntity += LogType.GenericEntity.OnReadingGenericEntity;

    var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(c => c);
    if (!string.IsNullOrEmpty(filter))
    {
        result = result.Where(filter);
    }
    var query = result.Take(numRecords).AsTableServiceQuery<GenericEntity>();
    IEnumerable<GenericEntity> res = query.Execute().ToList();

    return res;
}

对于我定义的所有表,我都有 TableServiceEntity 派生类型,因此我可以使用反射获取所有属性/类型。在动态 LINQ 查询中使用 GenericEntity 类进行过滤的问题是 GenericEntity 对象没有我试图过滤的任何属性,因为它们实际上只是字典条目(动态查询错误)。我可以解析出该特定类型的所有属性名称的过滤器并包装

"Property[" + propName + "]" 

围绕每个属性(通过使用类型解析器函数和反射找到)。然而,这似乎有点……矫枉过正。我试图找到一个更优雅的解决方案,但由于我实际上必须在 ServiceContext.CreateQuery 中提供一个类型,所以它有点困难。

所以我想我的最终问题是:我如何使用动态类或泛型类型和这个构造来利用动态查询进行过滤?这样我就可以从文本框中获取过滤器(例如“item_ID > 1023000”)并动态生成 TableServiceEntity 类型。

我可以利用其他方法来解决这个问题,但我想自从我开始使用动态 LINQ 以来,不妨试试动态类。

编辑: 所以我得到了由初始选择使用一些反射生成的动态类,但是在将 GenericEntity.Properties 的类型映射到各种关联表中时遇到了障碍记录类(TableServiceEntity 派生类)及其属性类型。主要问题仍然是我必须首先使用特定的数据类型来创建查询,所以我使用的是仅包含 KV 对的 GenericEntity 类型。这最终阻止了我进行过滤,因为我无法对对象类型进行比较运算符(>、

下面是我现在用来映射到动态类的代码:

var properties = newType./* omitted */.GetProperties(
    System.Reflection.BindingFlags.Instance |
    System.Reflection.BindingFlags.Public);

string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", Properties[\"{0}\"] as {0}", reflected.Name)).Substring(2) + ")";
var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(newSelect);

也许我应该只修改properties.Aggregate 方法,在“Properties[...]”部分加上reflected.PropertyType 前缀?所以新的选择字符串会变成这样:

string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", ({1})Properties[\"{0}\"] as {0}", reflected.Name, reflected.PropertyType)).Substring(2) + ")";

编辑 2: 所以现在我遇到了很大的障碍。我可以为所有表生成匿名类型以提取我需要的所有值,但是无论我为过滤器做什么,LINQ 都会对我产生影响。我已经说明了上面的原因(对象上没有比较运算符),但我现在一直在努力解决的问题是尝试为 Dynamic LINQ 扩展方法指定一个类型参数以接受新对象类型的架构。那里也不太走运……我会及时通知你们。

【问题讨论】:

  • 你好,我也遇到了同样的问题,想知道你是怎么解决的,能详细点吗?
  • 我的回答很好地解释了我是如何做到的,不幸的是代码太多,无法真正包含在评论中。下载 AzureTableQuery 项目的源代码,看看它是如何完成的。确切的方法是这样 -> 创建一个 GenericTableContext 对象并检索第一个 GenericEntity 对象。将此 GenericEntity 对象与查询文本一起传递给 QueryCompiler,后者会即时构建所需的实体并进行编译。我们得到编译后的程序集,并创建编译后的上下文对象的实例。这个上下文对象然后返回查询结果。

标签: c# linq generics dynamic azure


【解决方案1】:

我创建了一个简单的基于 System.Refection.Emit 的解决方案来创建您在运行时需要的类。 http://blog.kloud.com.au/2012/09/30/a-better-dynamic-tableserviceentity/

【讨论】:

  • 这一切都很好,但我的问题是无法在运行时创建类(我可以这样做)。我的问题与使用基本类型查询不同来源有关。这个问题最终变成了一个强类型问题,我无法引用那些特定的列,因为我们正在使用一个很可能没有我感兴趣的特定字段的基类。获取实体本身(即使是不同的类型)使用基类也有效,但导致问题的是查询。
【解决方案2】:

我遇到了完全相同的问题(使用几乎相同的代码:-))。我怀疑下面的 ADO.NET 类不知何故不与动态类型合作,但还没有找到确切的位置。

【讨论】:

  • 是的,我发现动态 LINQ 查询将尝试针对您指定的类运行,所以对我来说它是 GenericEntity 类型。如果我可以动态确定正确的表记录类型(我至少有 8 个)并基于该类型创建查询,那么我也许可以做到。想一想,在创建初始查询时,我可能只能在 CreateQuery 选择器函数中执行 Convert.ChangeType ,这样我在执行过滤器时就可以获得所需的类型......我会测试它出来告诉你结果。
【解决方案3】:

所以我找到了一种方法来做到这一点,但它不是很漂亮......

由于我无法在框架本身内真正做我想做的事,我利用了AzureTableQuery 项目中使用的一个概念。我几乎只有一个大的 C# 代码字符串,它可以使用我需要的确切对象即时编译。如果您查看AzureTableQuery 项目的代码,您会看到为我们拥有的任何表动态编译了一个单独的库,该库在我们查询表时通过并构建我们需要的所有属性和内容。不是最优雅或最轻量级的解决方案,但它仍然有效。

真心希望有更好的方法来做到这一点,但不幸的是,这并不像我希望的那么容易。希望有人能够从这次经历中学习,并可能找到更好的解决方案,但我已经有了我需要的东西,所以我已经完成了(目前)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多