Dynamic Expressions and Queries in LINQ

[索引页]




这篇是在LinqSamples中关于Dynamic LINQ的文档.今天看后就把它翻译过拉.不知道有没有官方的中文版,但我都翻译拉所以就发上来拉..如果你没有去ms下,你可以点 这里

请指正.

数据库应用程序常常依靠动态SQL查询,在运行时通过程序逻辑创建查询,这里的LINQ底层架构支持相似于通过使用在System.Linq.Expressions namespace中的类来动态创建表达式树.表达式树是一个适合多种不同的环境.但为另一方面基于字符串的方式来表示时,可能更为方便.这个DynamicExpression API扩展core LINQ API.这个API的位置是Dynamic.cs文件中.

(1) 字符串的动态编译产生expression trees.

(2) 动态创建数据类.

(3) 动态通过LINQ providers提供字符串查询.




Dynamic Expression API




这个Dynamic Expression API通过导入System.Linq.Dynamic命名空间.下面是一个应用Dynamic Expression API to a LINQ to SQL data source:

var query =
    db.Customers.
    Where("City = @0 and Orders.Count >= @1", "London", 10).
    OrderBy("CompanyName").
    Select("new(CompanyName as Name, Phone)");

先看ParseLambda方法:

System.Linq.Dynamic.DynamicExpression类定义下面重载ParseLambda方法为动态编译和创建lambda expressions.

public static LambdaExpression ParseLambda(
    ParameterExpression[] parameters, Type resultType,
    string expression, params object[] values);

public static LambdaExpression ParseLambda(
    Type argumentType, Type resultType,
    string expression, params object[] values);

public static Expression>
    ParseLambda(
        string expression, params object[] values);

第一个ParseLambda重载编译一个lambda expression关于给定参数和表达式body并且返回一个Expression> 实例描述这个结果.如果这个resultType参数是非null就是说它需要为表达式指定resultType.这个values的参数是支持0个或更多个为表达式所使用的替代值.

示例:

arameterExpression x = Expression.Parameter(typeof(int), "x");
ParameterExpression y = Expression.Parameter(typeof(int), "y");
LambdaExpression e = DynamicExpression.ParseLambda(
    new ParameterExpression[] { x, y }, null, "(x + y) * 2");

而创建和分配一个Expression>实例化为e去表示表达式(x + y) * 2.如果需要result type被指定,如

LambdaExpression e = DynamicExpression.ParseLambda(
    new ParameterExpression[] { x, y }, typeof(double), "(x + y) * 2");

编译时操作将包括隐式转换给一个resultType,在这个case已经产生一个Expression>实例.

这上第二个ParseLambda overload 编译一个lambda expression,使用一个单独的非命名指定argumentType参数.这个方法相比调用的第一个重载的ParseLambda而言一个参数(parameters argument)中就包括一个单独的ParameterExpression还包括空或null.

当编译一个lambda expression关于一个单独的非命名的参数时,这个非命名的参数的成员是自动在表达式字符串内,并且非命名的参数会提供给当前的实例并在当使用关键字时候能被引用.

LambdaExpression e = DynamicExpression.ParseLambda(
    typeof(Customer), typeof(bool),
    "City = @0 and Orders.Count >= @1",
    "London", 10);

创建和分配一个Expression> 实例e.注意City和Orders是Customer的成员.还注意替换值的使用,这里是应用于不变的值"London"和10.

第三个ParseLambda重写是泛性类型的版本.下面的事例就是产生的同样是Expression>实例如上面,当这里是一个静态类型.

Expression> e =
    DynamicExpression.ParseLambda(
        "City = @0 and Orders.Count >= @1",
        "London", 10);


The Parse Method





这个System.Linq.Dynamic.DynamicExpression 类定义为编译和创建表达式树片段下面方法:

public static Expression Parse(Type resultType, string expression,
    params object[] values);

如果这里的resultType参数是非nul,它就需要为表达式指定resultType,这里的Value参数将提供0或更多取代值给表达式使用,

不像ParseLambda方法,这个Parse方法返回一个表达树片段,,下面是使用Parse方法产生与上面示例同样的结果:

ParameterExpression x = Expression.Parameter(typeof(int), "x");
ParameterExpression y = Expression.Parameter(typeof(int), "y");
Dictionary symbols = new Dictionary();
symbols.Add("x", x);
symbols.Add("y", y);
Expression body = DynamicExpression.Parse(null, "(x + y) * 2", symbols);
LambdaExpression e = Expression.Lambda(
    body, new ParameterExpression[] { x, y });

注意这个Dictionary的使用,是使用来提供一个放有取代值的字典.这个字典来提供给表达式引用.


Substitution Values





在Dynamic Expression API中的几个方法都允许通过一个参数数组去指定取代值.取代值能(Substitution values )在表达式中被引用是使用一个@x标记来标识,x是参数数组的index.在参数数组的最后一个元素可能是一个实现IDictionary的一个对象.如果是,这个dictionary被在编译期间用来绘制标识符.

引用拉取代值的标识符的处理如下:

(1)如果值类型是System.Linq.Expressions.LambdaExpression,这个标识符必须触发dynamic lambda invocation.这里允许你和dynamic lambda expressions组合.

(2)另外,如果值的类型为System.Linq.Expressions.Expression,按照标识符使用表达式去取代.

(3)如果是使用Expression.Constant方法被使用来同个vulae来创建一个常量的表达式去替换标识符.

Dynamic Data Classes

一个数据类(data classes)是一个类还包括类的数据成员.这里的System.Linq.Dynamic.DynamicExpression 类里面定义拉下面方法来动态创建数据类.

public static Type CreateClass(params DynamicProperty[] properties);

public static Type CreateClass(IEnumerable properties);

这里的CreateClass方法是用来创建一个新的数据类,它是一个要给出一个public的属性集合和为新创建出来的类返回System.Type object.如果一个数据类已经创建一个同样的属性有序数据,那就可以一直接返回System.Type object.

数据类能实现private的实例变量和为指定属性实现read/write属性访问器.数据类还还可以重写Equeals和GetHashCode成员去实现值相等等功能.

数据类被在当前应用程序域中的内存中创建.所有数据继承System.Linq.Dynamic.DynamicClass和被自动产生时候给定的name应该考虑为private的(这里的name 将在应用程序域中被唯一标识,当不能访问应用程序的多个invoaction).注意一旦被创建,数据类就开始在当前当前应用程序的内存中开始存在.在当前还没有方法能动态的卸载动态创建的数据类.

这里的动态表达式编译器(dynamic expression parser)使用CreateClass方法从数据对象的初始者(data object initializers.)中产生类. 这里特性是常使用在动态的Select方法中去创建projections.

这个example使用CreateClass去创建一个有两个属性数据类,Name和Birthday并在之后使用.NET reflection创建一个类的实例和赋值给这些属性.



DynamicProperty[] props = new DynamicProperty[] {
    new DynamicProperty("Name", typeof(string)),
    new DynamicProperty("Birthday", typeof(DateTime)) };
Type type = DynamicExpression.CreateClass(props);
object obj = Activator.CreateInstance(type);
t.GetProperty("Name").SetValue(obj, "Albert", null);
t.GetProperty("Birthday").SetValue(obj, new DateTime(1879, 3, 14), null);
Console.WriteLine(obj);



IQueryable Extension Methods





在System.Linq.Dynamic.DynamicQueryable类中为动态查寻对象实现下面的扩展方法,都要实现IQueryable接口.

public static IQueryable Where(this IQueryable source,
    string predicate, params object[] values);

public static IQueryable Where(this IQueryable source,
    string predicate, params object[] values);

public static IQueryable Select(this IQueryable source,
    string selector, params object[] values);

public static IQueryable OrderBy(this IQueryable source,
    string ordering, params object[] values);

public static IQueryable OrderBy(this IQueryable source,
    string ordering, params object[] values);

public static IQueryable Take(this IQueryable source, int count);

public static IQueryable Skip(this IQueryable source, int count);

public static IQueryable GroupBy(this IQueryable source,
    string keySelector, string elementSelector, params object[] values);

public static bool Any(this IQueryable source);

public static int Count(this IQueryable source);

这些方法与相应他们的System.Linq.Queryable配对,除这个之外他们在还能使用IQueryable替换IQueryable和使用字符串替换lambda表达式去表达predicates,selectors,和orderings.IQueryable是一个非泛性基于IQueryable接口,所以这个方法能在当T在之前不知道是什么类型时就能被使用,也就是,当这个查询数据源被动态决定.(注意因为一个动态的predicate或ordering并不会影响resultType,泛性重载被提供来给Where和OrderBy保持使用强类型. )

这里predicate,selector,ordering,keySelector和elementSelector参数是字符串包括表达式被写在expression语言.在这个表达式字符串,这个当前实例的成员是在防卫内是自动能在四用关键字时被引用.

这里的OrderBy方法允许指定排序,用","分隔.每一个","后面都可以跟asc或ascending表示升序,或desc或descending表示降序.下面是一个升序.

products.OrderBy("Category.CategoryName, UnitPrice descending");

上面是按unit price降序.


The ParseException Class





动态表达式API报告编译错误使用System.Linq.Dynamic.ParseException 类.这个ParseException class的Position属性能给在表达式字符串中编译错误触发的字符的index.


Expression Language





Expression Language实现Dynamic ExpressionAPI提供一个简单且方便方式写表达式转换成LINQ表达式树.这个方面语言支持大部分表达式树的结构,但它不是一个完整的查询或程序语言.特别的是,这个表达式语言不支持声明或语句.

Identifiers
标识符号,一个标识符号由一个字符或下划线包括任何数字或字符.或下划线.一个关键词是同样的拼写参考一个标识符,这个标识符必须有一个@字符做外前缀.

x   Hello   m_1   @true   @String

@x是标识符,x是一个比0大的整数,使用来表示取代值(substitution values),如果即便通过表达式编译器编译.如下面事例:

customers.Where("Country = @0", country);

包装对标识符或关键字没有什么重要意义.


Literals





表达式语言支持integer, real, string,和 character.


下面都是熟悉的..我就不翻译:

Literals

The expression language supports integer, real, string, and character literals.

An integer literal consists of a sequence of digits. The type of an integer literal is the first of the types Int32, UInt32, Int64, or UInt64 that can represent the given value. An integer literal implicitly converts to any other numeric type provided the number is in the range of that type. Some examples of integer literals:

0   123   10000

A real literal consists of an integral part followed by a fractional part and/or an exponent. The integral part is a sequence of one or more digits. The fractional part is a decimal point followed by one or more digits. The exponent is the letter e or E followed by an optional + or – sign followed by one or more digits. The type of a real literal is Double. A real literal implicitly converts to any other real type provided the number is in the range of that type. Some examples of real literals:

1.0   2.25   10000.0   1e0   1e10   1.2345E-4

A string literal consists of zero or more characters enclosed in double quotes. Inside a string literal, a double quote is written as two consecutive double quotes. The type of a string literal is String. Some examples of string literals:

"hello"   ""    """quoted"""   "'"

A character literal consists of a single character enclosed in single quotes. Inside a character literal, a single quote is written as two consecutive single quotes. The type of a character literal is Char. Some examples of character literals:

'A'   '1'   ''''   '"'

Constants

The predefined constants true and false denote the two values of the type Boolean.

The predefined constant null denotes a null reference. The null constant is of type Object, but is also implicitly convertible to any reference type.

Types

The expression language defines the following primitive types:

Object                Boolean             Char                     String                SByte                   Byte
Int16                   UInt16                Int32                   UInt32                Int64                   UInt64
Decimal     Single                Double                DateTime           TimeSpan           Guid

The primitive types correspond to the similarly named types in the System namespace of the .NET Framework Base Class Library. The expression language also defines a set of accessible types consisting of the primitive types and the following types from the System namespace:

Math                     Convert

The accessible types are the only types that can be explicitly referenced in expressions, and method invocations in the expression language are restricted to methods declared in the accessible types.

The nullable form of a value type is referenced by writing a ? after the type name. For example, Int32? denotes the nullable form of Int32.

The non-nullable and nullable forms of the types SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, and UInt64 are collectively called the integral types.

The non-nullable and nullable forms of the types Single, Double, and Decimal are collectively called the real types.

The integral types and real types are collectively called the numeric types.

Conversions

The following conversions are implicitly performed by the expression language:

·         From the the null literal to any reference type or nullable type.

·         From an integer literal to an integral type or real type provided the number is within the range of that type.

·         From a real literal to a real type provided the number is within the range of that type.

·         From a string literal to an enum type provided the string literal contains the name of a member of that enum type.

·         From a source type that is assignment compatible with the target type according to the Type.IsAssignableFrom method in .NET.

·         From a non-nullable value type to the nullable form of that value type.

·         From a numeric type to another numeric type with greater range.

The expression language permits explicit conversions using the syntax type(expr), where type is a type name optionally followed by ? and expr is an expression. This syntax may be used to perform the following conversions:

·         Between two types provided Type.IsAssignableFrom is true in one or both directions.

·         Between two types provided one or both are interface types.

·         Between the nullable and non-nullable forms of any value type.

·         Between any two types belonging to the set consisting of SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Decimal, Single, Double, Char, any enum type, as well as the nullable forms of those types.

Operators

The table below shows the operators supported by the expression language in order of precedence from highest to lowest. Operators in the same category have equal precedence. In the table, x, y, and z denote expressions, T denotes a type, and m denotes a member.

Category

Expression

Description

Primary

x.m

Instance field or instance property access. Any public field or property can be accessed.

x.m(…)

Instance method invocation. The method must be public and must be declared in an accessible type.

x[…]

Array or indexer access. Multi-dimensional arrays are not supported.

T.m

Static field or static property access. Any public field or property can be accessed.

T.m(…)

Static method invocation. The method must be public and must be declared in an accessible type.

T(…)

Explicit conversion or constructor invocation. Note that new is not required in front of a constructor invocation.

new(…)

Data object initializer. This construct can be used to perform dynamic projections.

it

Current instance. In contexts where members of a current object are implicitly in scope, it is used to refer to the entire object itself.

x(…)

Dynamic lambda invocation. Used to reference another dynamic lambda expression.

iif(x, y, z)

Conditional expression. Alternate syntax for x ? y : z.

Unary

-x

Negation. Supported types are Int32, Int64, Decimal, Single, and Double.

!x

not x

Logical negation. Operand must be of type Boolean.

Multiplicative

x * y

Multiplication. Supported types are Int32, UInt32, Int64, UInt64, Decimal, Single, and Double.

x / y

Division. Supported types are Int32, UInt32, Int64, UInt64, Decimal, Single, and Double.

x % y

x mod y

Remainder. Supported types are Int32, UInt32, Int64, UInt64, Decimal, Single, and Double.

Additive

x + y

Addition or string concatenation. Performs string concatenation if either operand is of type String. Otherwise, performs addition for the supported types Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, and TimeSpan.

x – y

Subtraction. Supported types are Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, and TimeSpan.

x & y

String concatenation. Operands may be of any type.

Relational

x = y

x == y

Equal. Supported for reference types and the primitive types. Assignment is not supported.

x != y

x <> y

Not equal. Supported for reference types and the primitive types.

x < y

Less than. Supported for all primitive types except Boolean, Object and Guid.

x > y

Greater than. Supported for all primitive types except Boolean, Object and Guid.

x <= y

Less than or equal. Supported for all primitive types except Boolean, Object and Guid.

x >= y

Greater than or equal. Supported for all primitive types except Boolean, Object and Guid.

Logical AND

x && y

x and y

Logical AND. Operands must be of type Boolean.

Logical OR

x || y

x or y

Logical OR. Operands must be of type Boolean.

Conditional

x ? y : z

Evaluates y if x is true, evaluates z if x is false.



我们来看:


Method and Constructor Invocations





表达式树语言限制在可访问类型中定义public方法和构造器的调用.这个限制现有保护对于任意的方法调用没有副作用.

重载方法,构造器,和索引器使用C#熟悉的规则.在非正式领域中,重载将挖掘最匹配的方法,构造器,或索引器,或如果没有单个最佳的方匹配就会报告一个错误.

注意 构造器调用没有前缀New,下面是创建一个DateTime实例指定规格为,年,月,日.

orders.Where("OrderDate >= DateTime(2007, 1, 1)");


Data Object Initializers




一个Data Object Initializers创建一个数据类并且返回这个类的实例.这个数据类的属性是通过data object initializer来推断的.明确的一个data object initializer.

new(e1 as p1, e2 as p2, e3 as p3)

创建一个有三个属性p1,p2和p3的数据类.它能通过表达式e1, e2和e3来推断他们的类型,并且返回一个数据对象相关属性初始化从e1,e2,e3中捕获值的一实例.一个属性初始器能忽略如关键字和提供与表达式关联的是一个域或者属性的属性名的访问.(A property initializer may omit the as keyword and the property name provided the associated expression is a field or property access. )

customers.Select("new(CompanyName as Name, Phone)");

创建一个有两个属性的数据类,Name和Phone,并且返回一个被初始化数据类的有序数据的实例.

Current Instance

当正在编译关于 一个单独非命名参数(a single unnamed parameter)的lambda expression时,这个单独非命名参数(a single unnamed parameter)的成员在表达式字符串的范围中是自动被引用的并且当使用关键字时Current Instance的单独非命名参数会被引用.

customers.Where("Country = @0", country);

相当于

customers.Where("it.Country = @0", country);

这个IQueryable extension methods 有所有编译他们的表达式参数如lambda expressions with a single unnamed parameter.


Dynamic Lambda Invocation





一个表达式能通过dynamic lambda invocations来引用其他的动态lambda 表达式.一个dynamic lambda invocation有一个参考System.Linq.Expressions.LambdaExpression的实例子的取代变量标识符组成,下面是一个argument list.这个必须供给dynamic lambda expression参数列表与arguments一致.

下面编译两分离的dynamic lambda expressions 和通过dynamic lambda invocations在一个predicate expression联合他们.

当然也可以就结合static和dynamic lambda expressions:

Expression> e1 =
    c => c.City == "London";
Expression> e2 =
    DynamicExpression.ParseLambda("Orders.Count >= 10");
IQueryable query =
    db.Customers.Where("@0(it) and @1(it)", e1, e2);

上面两个示例的效果:

IQueryable query =
    db.Customers.Where(c => c.City == "London" && c.Orders.Count >= 10);

 operators

是标准操作符号的子集,对象要实现IEnumerable.明确,下面创建方式被允许,where seq is an IEnumerable instance, predicate is a boolean expression, and selector is an expression of any type:

seq . Where ( predicate )                            seq . Any ( )

seq . Any ( predicate )                                 seq . All ( predicate )

seq . Count ( )                                                seq . Count ( predicate )

seq . Min ( selector )                                    seq . Max ( selector )

seq . Sum ( selector )                                    seq . Average ( selector )

在predicate 和selector expressions中,这个current instance 的成员内有序数据操作符在范围内是自动生成的,并且实例自己能在关键字被使用时被引用.

customers.Where("Orders.Any(Total >= 1000)");


Enum type suppor




expression language 支持从一个string literal 隐式转换为enum类型并提供string literal 中包括的enum类型类型的成员名.

orders.Where("OrderDate.DayOfWeek = \"Monday\"");

等同于

orders.Where("OrderDate.DayOfWeek = @0", DayOfWeek.Monday);



 


 



 

你可能感兴趣的:(Dynamic Expressions and Queries in LINQ)