【问题标题】:Extracting the value from a Member Expression that contains a parameter从包含参数的成员表达式中提取值
【发布时间】:2014-12-03 17:38:27
【问题描述】:

如何从成员表达式中提取值,其中成员表达式中的表达式不是常量,而是参数表达式。

我正在为我们公司构建一个小型 Linq to MDX ORM。

在我的生成器模板中,在数据库中找到的每个维度都是一个类,并且在每个维度中,都有为该维度找到的 Attribute 属性。在生成所有维度类之后,将生成更高级别的Cube 类,其中包含作为属性的维度以及该多维数据集的度量。生成所有多维数据集类后,将构建包含作为Cube<MyCube> 属性生成的多维数据集类的最终类,其中Cube<> 是我的IQueryable 类型。这是一个例子。

//Generated cs file example:
public partial MyDatabase : CubeBase
{
    //Some base implementation goes here        

    public Cube<FooCube> FooCube { get { return new Cube<FirstCube>(new Provider("connection string"); } }

    //Any other cubes would follow here that were found on this database.
} 

//A calendar dimension
public class FooCube_CalendarDimension
{
    public Attribute WeekEnding { get { return new Attribute("[Calendar].[Week Ending]"); } }

    public Attribute Month { get { return new Attribute("[Calendar].[Month]"); } }

    public Attribute Year { get { return new Attribute("[Calendar].[Year]"); } }
}

//The "FooCube" 
public partial class FooCube
{
    //List Dimensions
    public FooCube_Calendar_Dimension Calendar { get { return new FooCube_Calendar_Dimension(); }     }
    //Other dimensions here

    [Measure]
    public string RetailDollars { get { return "[Retail Dollars]"; } }
    // Other measures here
}

现在,一个非常基本的 linq 查询示例来查询多维数据集:

//using MyDatabase = db
var mdx = from cube in db.FooCube
          where cube.Calendar.Year == "2014"
          select new
          {
              Month = cube.Calendar.Month.Children
              Dollars = cube.RetailDollars
          }

例如,我试图从 cube.Calendar.Month.Children 中获取值,该值来自 Attribute 对象,该对象是 FooCube_Calendar_Demsion 类的属性,它本身就是“FooCube”类中的属性.

我尝试了Access the value of a member expression 的答案,但在尝试编译 lambda 表达式时出现错误“未引用 'cube' 参数”。它传递给属性类的构造函数的值存储在一个属性中,这就是我想要访问的值(其中之一)。

【问题讨论】:

  • 您想从该查询中获得什么价值,具体而言?该查询中有很多成员表达式;你需要清楚你要评估哪些。
  • Attribute 类中有一个“Tag”属性。这就是“[Calendar].[Month]”字符串的存储位置。还有一个“Children”属性,它简单地将“.CHILDREN”连接到值并返回“[Calendar].[Month].CHILDREN。这些是我想要的值。
  • 您应该为您的属性使用 C# 属性,以获取有关在创建查询时使用的属性的信息。从属性返回的值应该是查询的结果,而不是创建查询时使用的值。当您的属性在其上使用 C# 属性时,您可以检查这些值,而无需拥有这些类型的特定实例。在这里以 EF 为例。
  • 这就是我所采用的方法,例如 [Tag(Tag="[Calendar].[Month]")],效果很好,但我也想让用户能够添加在另一个部分类中查询范围内的用户成员和集合,以便他们可以在 linq 查询中调用它们,我希望他们能够使用生成的对象来这样做。此外,我希望用户能够使用 linq 查询中的“let”表达式即时添加这些计算成员。能够做到这一点的唯一方法是以某种方式从这些挑剔的成员表达式中提取值。

标签: c# linq expression mdx expression-trees


【解决方案1】:

基本上,你不能。至少,不是以任何明智的方式。目前您所拥有的只是一个查询。您实际上并没有对象的集合,您只知道创建这些对象需要做什么。查询提供者的工作是您当前正在编写的过程中,以实际构建查询定义的对象并返回它们。

您已经设计了您的程序,以便创建对象的查询提供程序需要已经创建的对象才能正确构建查询。不可能已经拥有由查询定义的对象您还没有构建。你已经为自己创建了一个循环依赖。

提供构建查询所需的信息很重要,而不是在实例中查询本身创建的对象。通常这是通过属性上的属性来完成的,或者通过基于其他现有 C# 元数据的关于类型本身的查询来完成。这种类型的数据存在,并且可供您的查询提供程序访问,而无需您负责创建的对象的任何实际实例

【讨论】:

  • 这是有道理的。我对表达式和 linq 查询仍然很陌生,但我理解你在说什么。如果没有整个用户创建的成员和设置的东西,我会很高兴。也许我只需要以不同的方式实现这些。
【解决方案2】:

我添加这个答案是因为我找到了两种提取我想要的值的方法。不过,我要感谢 Servey 的回答,因为他确实没有以任何明智的方式做这件事,因为我编写了我的代码。

我发现有两种解决方法。

  1. 使用具有泛型参数的泛型转换器类,该泛型参数是我的 lambda 表达式所需的参数的类类型,然后使用该泛型参数作为 Func&lt;object, T&gt; delgate 上的输入参数。这是最好和最快的方法,因为在运行时没有动态操作。

var lambda = Expression.Lambda&lt;Func&lt;object, T&gt;&gt;(//Expressions here);

  1. 我发现的第二种方法比较慢,因为它涉及Delegate.DynamicInvoke() 方法,但它确实有效。

    var lambda = Expression.Lambda(member, (ParameterExpression)member.Expression); var d = lambda.Compile(); return d.DynamicInvoke(member.Expression.Type.GetConstructors()[0].Invoke(new object[0]));

这将获得对象的值,但由于动态调用而代价高昂。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    • 2019-01-02
    • 1970-01-01
    • 2021-12-16
    • 2012-09-12
    相关资源
    最近更新 更多