Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / Translator.cs / 3 / Translator.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....], [....]
//---------------------------------------------------------------------
using System.Data.Common.CommandTrees;
using System.Collections.Generic;
using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
using LinqExpression = System.Linq.Expressions.Expression;
using System.Diagnostics;
using System.Data.Metadata.Edm;
using System.Linq.Expressions;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity;
namespace System.Data.Objects.ELinq
{
internal sealed partial class ExpressionConverter
{
// Base class supporting the translation of LINQ node type(s) given a LINQ expression
// of that type, and the "parent" translation context (the ExpressionConverter processor)
private abstract class Translator
{
private readonly ExpressionType[] _nodeTypes;
protected Translator(params ExpressionType[] nodeTypes)
{
_nodeTypes = nodeTypes;
}
// Gets LINQ node types this translator should be registed to process.
internal IEnumerable NodeTypes { get { return _nodeTypes; } }
internal abstract CqtExpression Translate(ExpressionConverter parent, LinqExpression linq);
public override string ToString()
{
return this.GetType().Name;
}
}
#region Misc
// Typed version of Translator
private abstract class TypedTranslator : Translator
where T_Linq : LinqExpression
{
protected TypedTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq)
{
return TypedTranslate(parent, (T_Linq)linq);
}
protected abstract CqtExpression TypedTranslate(ExpressionConverter parent, T_Linq linq);
}
private sealed class ConstantTranslator
: TypedTranslator
{
internal ConstantTranslator()
: base(ExpressionType.Constant) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.ConstantExpression linq)
{
ObjectQuery queryOfT = linq.Value as ObjectQuery;
if (null != queryOfT)
{
return parent.TranslateInlineQueryOfT(queryOfT);
}
bool isNullValue = null == linq.Value;
// Remove facet information: null instances do not constrain type facets (e.g. a null string does not restrict
// "length" in compatibility checks)
TypeUsage type;
bool typeSupported = false;
if (parent.TryGetValueLayerType(linq.Type, out type))
{
// For constant values, support only primitive type (this is all that is supported by CQTs)
// For null types, also allow EntityType. Although other types claim to be supported, they
// don't work (e.g. complex type, see SQL BU 543956)
if (type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType ||
(isNullValue && type.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType))
{
typeSupported = true;
}
}
if (!typeSupported)
{
throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type), Strings.ELinq_PrimitiveTypesSample));
}
// create a constant or null expression depending on value
if (isNullValue)
{
return parent._commandTree.CreateNullExpression(type);
}
else
{
return parent._commandTree.CreateConstantExpression(linq.Value, type);
}
}
}
private sealed class MemberAccessTranslator
: TypedTranslator
{
internal MemberAccessTranslator()
: base(ExpressionType.MemberAccess) { }
// attempt to translate the member access to a "regular" property, a navigation property, or a calculated
// property
protected override CqtExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq)
{
CqtExpression propertyExpression;
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Member, out memberName, out memberType);
// note: we check for "regular" properties last, since the other two flavors derive
// from this one
if (linq.Expression != null)
{
CqtExpression instance = parent.TranslateExpression(linq.Expression);
if (TryResolveAsProperty(parent, memberInfo,
instance.ResultType, instance, out propertyExpression))
{
return propertyExpression;
}
}
if (memberInfo.MemberType == MemberTypes.Property)
{
// Check whether it is one of the special proeperties that we know how to translate
PropertyTranslator propertyTranslator;
if (TryGetTranslator((PropertyInfo)memberInfo, out propertyTranslator))
{
return propertyTranslator.Translate(parent, linq);
}
}
// no other property types are supported by LINQ over entities
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnrecognizedMember(linq.Member.Name));
}
#region Static members and initializers
private static readonly Dictionary s_propertyTranslators;
private static bool s_vbPropertiesInitialized;
private static readonly object s_vbInitializerLock = new object();
static MemberAccessTranslator()
{
// initialize translators for specific properties
s_propertyTranslators = new Dictionary();
foreach (PropertyTranslator translator in GetPropertyTranslators())
{
foreach (PropertyInfo property in translator.Properties)
{
s_propertyTranslators.Add(property, translator);
}
}
}
///
/// Tries to get a translator for the given property info.
/// If the given property info corresponds to a Visual Basic property,
/// it also initializes the Visual Basic translators if they have not been initialized
///
///
///
///
private static bool TryGetTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator)
{
//If the type is generic, we try to match the generic property
if (propertyInfo.DeclaringType.IsGenericType)
{
try
{
propertyInfo = propertyInfo.DeclaringType.GetGenericTypeDefinition().GetProperty(propertyInfo.Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
}
catch (AmbiguousMatchException)
{
propertyTranslator = null;
return false;
}
if (propertyInfo == null)
{
propertyTranslator = null;
return false;
}
}
if (s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator))
{
return true;
}
// check if this is the visual basic assembly
if (s_visualBasicAssemblyFullName == propertyInfo.DeclaringType.Assembly.FullName)
{
lock (s_vbInitializerLock)
{
if (!s_vbPropertiesInitialized)
{
InitializeVBProperties(propertyInfo.DeclaringType.Assembly);
s_vbPropertiesInitialized = true;
}
// try again
return s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator);
}
}
propertyTranslator = null;
return false;
}
// Determines if the given property can be resolved as a standard or navigation property.
private static bool TryResolveAsProperty(ExpressionConverter parent,
MemberInfo clrMember, TypeUsage definingType, CqtExpression instance, out CqtExpression propertyExpression)
{
// retrieve members directly from row types, which are not mapped between O and C
RowType rowType = definingType.EdmType as RowType;
string name = clrMember.Name;
if (null != rowType)
{
EdmMember member;
if (rowType.Members.TryGetValue(name, false, out member))
{
propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance);
return true;
}
propertyExpression = null;
return false;
}
// for non-row structural types, map from the O to the C layer using the perspective
StructuralType structuralType = definingType.EdmType as StructuralType;
if (null != structuralType)
{
EdmMember member = null;
if (parent._typeResolver.Perspective.TryGetMember(structuralType, name, false, out member))
{
if (null != member)
{
if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
{
NavigationProperty navProp = (NavigationProperty)member;
propertyExpression = TranslateNavigationProperty(parent, clrMember, instance, navProp);
return true;
}
else
{
// if we're accessing a key member of a navigation, collapse the structure instance
// to the key reference.
instance = NormalizeInstanceForReference(parent, instance, member);
propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance);
return true;
}
}
}
}
// try to unwrap GroupBy "Key" member
if (name == ExpressionConverter.KeyColumnName)
{
// see if we can "unwrap" the current instance
if (DbExpressionKind.Property == instance.ExpressionKind)
{
DbPropertyExpression property = (DbPropertyExpression)instance;
InitializerMetadata initializerMetadata;
// if we're dealing with the "Group" property of a GroupBy projection, we know how to unwrap
// it
if (property.Property.Name == ExpressionConverter.GroupColumnName && // only know how to unwrap the group
InitializerMetadata.TryGetInitializerMetadata(property.Instance.ResultType, out initializerMetadata) &&
initializerMetadata.Kind == InitializerMetadataKind.Grouping)
{
propertyExpression = parent._commandTree.CreatePropertyExpression(ExpressionConverter.KeyColumnName,
property.Instance);
return true;
}
}
}
propertyExpression = null;
return false;
}
///
/// We simplify the property instance where the user is accessing a key member of
/// a reference navigation. The instance becomes simply the reference key in such
/// cases.
///
/// For instance, product.Category.CategoryID becomes Ref(product.Category).CategoryID,
/// which gives us a chance of optimizing the query (using foreign keys rather than joins)
///
/// Conversion context.
/// Instance from which we're accessing member.
/// Member we're accessing.
/// 'Simplified' instance. If the member is a key and the instance is a navigation
/// the returned instance is a reference navigation rather than the full entity.
private static CqtExpression NormalizeInstanceForReference(ExpressionConverter parent, CqtExpression instance, EdmMember member)
{
EdmType instanceType = instance.ResultType.EdmType;
if (instanceType.BuiltInTypeKind == BuiltInTypeKind.EntityType)
{
EntityType entityType = (EntityType)instanceType;
if (entityType.KeyMembers.Contains(member) && instance.ExpressionKind == DbExpressionKind.Property)
{
DbPropertyExpression propertyExpression = (DbPropertyExpression)instance;
if (propertyExpression.Property.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
{
// modify the property expression so that it merely retrieves the reference
// not the entire entity
NavigationProperty navigationProperty = (NavigationProperty)propertyExpression.Property;
DbExpression navigationSource = parent._commandTree.CreateEntityRefExpression(propertyExpression.Instance);
DbExpression navigationExpression = parent._commandTree.CreateRelationshipNavigationExpression(
navigationProperty.FromEndMember,
navigationProperty.ToEndMember,
navigationSource);
instance = parent._commandTree.CreateRefKeyExpression(navigationExpression);
}
}
}
return instance;
}
private static CqtExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, CqtExpression instance, NavigationProperty navProp)
{
CqtExpression propertyExpression;
propertyExpression = parent._commandTree.CreatePropertyExpression(navProp, instance);
// for EntityCollection navigations, wrap in "grouping" where the key is the parent
// entity and the group contains the child entities
if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind)
{
List> collectionColumns =
new List>(2);
collectionColumns.Add(new KeyValuePair(
ExpressionConverter.EntityCollectionOwnerColumnName, instance));
collectionColumns.Add(new KeyValuePair(
ExpressionConverter.EntityCollectionElementsColumnName, propertyExpression));
propertyExpression = parent.CreateNewRowExpression(collectionColumns,
InitializerMetadata.CreateEntityCollectionInitializer(parent.EdmItemCollection, ((PropertyInfo)clrMember).PropertyType, navProp));
}
return propertyExpression;
}
private static void InitializeVBProperties(Assembly vbAssembly)
{
Debug.Assert(!s_vbPropertiesInitialized);
foreach (PropertyTranslator translator in GetVisualBasicPropertyTranslators(vbAssembly))
{
foreach (PropertyInfo property in translator.Properties)
{
s_propertyTranslators.Add(property, translator);
}
}
}
private static IEnumerable GetVisualBasicPropertyTranslators(Assembly vbAssembly)
{
yield return new VBDateAndTimeNowTranslator(vbAssembly);
}
private static IEnumerable GetPropertyTranslators()
{
yield return new DefaultCanonicalFunctionPropertyTranslator();
yield return new RenameCanonicalFunctionPropertyTranslator();
yield return new EntityCollectionCountTranslator();
yield return new NullableHasValueTranslator();
yield return new NullableValueTranslator();
}
///
/// Checks whether the given property info can be translated into a canonical function.
/// This method is used to determine whether client side evaluation should be done,
/// if the property can be evaluated in the store, it is not being evaluated on the client
///
///
///
internal static bool CanTranslatePropertyInfo(PropertyInfo propertyInfo)
{
PropertyTranslator propertyTranslator;
return TryGetTranslator(propertyInfo, out propertyTranslator);
}
#endregion
#region Property Translators
private abstract class PropertyTranslator
{
private readonly IEnumerable _properties;
protected PropertyTranslator(params PropertyInfo[] properties) { _properties = properties; }
protected PropertyTranslator(IEnumerable properties) { _properties = properties; }
internal IEnumerable Properties { get { return _properties; } }
internal abstract CqtExpression Translate(ExpressionConverter parent, MemberExpression call);
public override string ToString()
{
return GetType().Name;
}
}
private sealed class DefaultCanonicalFunctionPropertyTranslator : PropertyTranslator
{
internal DefaultCanonicalFunctionPropertyTranslator()
: base(GetProperties()) { }
private static IEnumerable GetProperties()
{
yield return typeof(String).GetProperty("Length", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
}
// Default translator for method calls into canonical functions.
// Translation:
// object.PropertyName -> PropertyName(object)
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
return parent.TranslateIntoCanonicalFunction(call.Member.Name, call, call.Expression);
}
}
private sealed class RenameCanonicalFunctionPropertyTranslator : PropertyTranslator
{
private static readonly Dictionary s_propertyRenameMap = new Dictionary(2);
internal RenameCanonicalFunctionPropertyTranslator()
: base(GetProperties()) { }
private static IEnumerable GetProperties()
{
yield return GetProperty(typeof(DateTime), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTime);
yield return GetProperty(typeof(DateTime), "UtcNow", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentUtcDateTime);
yield return GetProperty(typeof(DateTimeOffset), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTimeOffset);
yield return GetProperty(typeof(TimeSpan), "Hours", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Hour);
yield return GetProperty(typeof(TimeSpan), "Minutes", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Minute);
yield return GetProperty(typeof(TimeSpan), "Seconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Second);
yield return GetProperty(typeof(TimeSpan), "Milliseconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Millisecond);
}
private static PropertyInfo GetProperty(Type declaringType, string propertyName, BindingFlags bindingFlages, string canonicalFunctionName)
{
PropertyInfo propertyInfo = declaringType.GetProperty(propertyName, bindingFlages);
s_propertyRenameMap.Add(propertyInfo, canonicalFunctionName);
return propertyInfo;
}
// Translator for static properties into canonical functions when there is a corresponding
// canonical function but with a differnet name
// Translation:
// object.PropertyName -> CanonicalFunctionName(object)
// Type.PropertyName -> CanonicalFunctionName()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
PropertyInfo property = (PropertyInfo)call.Member;
String canonicalFunctionName = s_propertyRenameMap[property];
CqtExpression result;
if (call.Expression == null)
{
result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call);
}
else
{
result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call, call.Expression);
}
return result;
}
}
private sealed class VBDateAndTimeNowTranslator : PropertyTranslator
{
private const string s_dateAndTimeTypeFullName = "Microsoft.VisualBasic.DateAndTime";
internal VBDateAndTimeNowTranslator(Assembly vbAssembly)
: base(GetProperty(vbAssembly)) { }
private static PropertyInfo GetProperty(Assembly vbAssembly)
{
return vbAssembly.GetType(s_dateAndTimeTypeFullName).GetProperty("Now", BindingFlags.Public | BindingFlags.Static);
}
// Translation:
// Now -> GetDate()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
return parent.TranslateIntoCanonicalFunction(ExpressionConverter.CurrentDateTime, call);
}
}
private sealed class EntityCollectionCountTranslator : PropertyTranslator
{
internal EntityCollectionCountTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(EntityCollection<>).GetProperty(ExpressionConverter.s_entityCollectionCountPropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// EntityCollection.Count -> Count()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
// retranslate as a Count() aggregate, since the name collision prevents us
// from calling the method directly in VB and C#
MethodInfo countMethod;
ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod);
Debug.Assert(null != countMethod, "Count() must exist");
countMethod = countMethod.MakeGenericMethod(call.Member.DeclaringType.GetGenericArguments());
LinqExpression countCall = LinqExpression.Call(countMethod, call.Expression);
return parent.TranslateExpression(countCall);
}
}
private sealed class NullableHasValueTranslator : PropertyTranslator
{
internal NullableHasValueTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableHasValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// Nullable.HasValue -> Not(IsNull(arg))
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
CqtExpression argument = parent.TranslateExpression(call.Expression);
Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
return parent._commandTree.CreateNotExpression(parent.CreateIsNullExpression(argument, call.Expression.Type));
}
}
private sealed class NullableValueTranslator : PropertyTranslator
{
internal NullableValueTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// Nullable.Value -> arg
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
CqtExpression argument = parent.TranslateExpression(call.Expression);
Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
return argument;
}
}
#endregion
}
private sealed class ParameterTranslator
: TypedTranslator
{
internal ParameterTranslator()
: base(ExpressionType.Parameter) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq)
{
// Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression)
if (parent._bindingContext.IsRootContextParameter(linq))
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter(linq.Name));
}
else
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnboundParameterExpression(linq.Name));
}
}
}
private sealed class NewTranslator
: TypedTranslator
{
internal NewTranslator()
: base(ExpressionType.New) { }
protected override DbExpression TypedTranslate(ExpressionConverter parent, NewExpression linq)
{
int memberCount = null == linq.Members ? 0 : linq.Members.Count;
if (null == linq.Constructor ||
linq.Arguments.Count != memberCount)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
}
parent.CheckInitializerType(linq.Type);
List> recordColumns =
new List>(memberCount + 1);
HashSet memberNames = new HashSet(StringComparer.Ordinal);
for (int i = 0; i < memberCount; i++)
{
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Members[i], out memberName, out memberType);
CqtExpression memberValue = parent.TranslateExpression(linq.Arguments[i]);
memberNames.Add(memberName);
recordColumns.Add(new KeyValuePair(memberName, memberValue));
}
InitializerMetadata initializerMetadata;
if (0 == memberCount)
{
// add a sentinel column because CQTs do not accept empty row types
recordColumns.Add(new KeyValuePair(KeyColumnName, parent._commandTree.CreateTrueExpression()));
initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq);
}
else
{
// Construct a new initializer type in metadata for this projection (provides the
// necessary context for the object materializer)
initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq);
}
parent.ValidateInitializerMetadata(initializerMetadata);
DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
return projection;
}
}
private sealed class MemberInitTranslator
: TypedTranslator
{
internal MemberInitTranslator()
: base(ExpressionType.MemberInit) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
{
if (null == linq.NewExpression.Constructor ||
0 != linq.NewExpression.Constructor.GetParameters().Length)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
}
parent.CheckInitializerType(linq.Type);
List> recordColumns =
new List>(linq.Bindings.Count + 1);
MemberInfo[] members = new MemberInfo[linq.Bindings.Count];
HashSet memberNames = new HashSet(StringComparer.Ordinal);
for (int i = 0; i < linq.Bindings.Count; i++)
{
MemberAssignment binding = linq.Bindings[i] as MemberAssignment;
if (null == binding)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedBinding);
}
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(binding.Member, out memberName, out memberType);
CqtExpression memberValue = parent.TranslateExpression(binding.Expression);
memberNames.Add(memberName);
members[i] = memberInfo;
recordColumns.Add(new KeyValuePair(memberName, memberValue));
}
InitializerMetadata initializerMetadata;
if (0 == recordColumns.Count)
{
// add a sentinel column because CQTs do not accept empty row types
recordColumns.Add(new KeyValuePair(KeyColumnName, parent._commandTree.CreateTrueExpression()));
initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq.NewExpression);
}
else
{
// Construct a new initializer type in metadata for this projection (provides the
// necessary context for the object materializer)
initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq, members);
}
parent.ValidateInitializerMetadata(initializerMetadata);
DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
return projection;
}
}
private sealed class ConditionalTranslator : TypedTranslator
{
internal ConditionalTranslator()
: base(ExpressionType.Conditional) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, ConditionalExpression linq)
{
// translate Test ? IfTrue : IfFalse --> CASE WHEN Test THEN IfTrue ELSE IfFalse
List whenExpressions = new List(1);
whenExpressions.Add(parent.TranslateExpression(linq.Test));
List thenExpressions = new List(1);
thenExpressions.Add(parent.TranslateExpression(linq.IfTrue));
CqtExpression elseExpression = parent.TranslateExpression(linq.IfFalse);
return parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, elseExpression);
}
}
private sealed class NotSupportedTranslator : Translator
{
internal NotSupportedTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes)
{
}
internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType));
}
}
#endregion
#region Binary expression translators
private abstract class BinaryTranslator
: TypedTranslator
{
protected BinaryTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq);
}
protected abstract CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq);
}
private sealed class CoalesceTranslator : BinaryTranslator
{
internal CoalesceTranslator()
: base(ExpressionType.Coalesce) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
// left ?? right gets translated to:
// CASE WHEN IsNull(left) THEN right ELSE left
// construct IsNull
CqtExpression isNull = parent.CreateIsNullExpression(left, linq.Left.Type);
// construct case expression
List whenExpressions = new List(1);
whenExpressions.Add(isNull);
List thenExpressions = new List(1);
thenExpressions.Add(right);
CqtExpression caseExpression = parent._commandTree.CreateCaseExpression(whenExpressions,
thenExpressions, left);
return caseExpression;
}
}
private sealed class AndAlsoTranslator : BinaryTranslator
{
internal AndAlsoTranslator()
: base(ExpressionType.AndAlso) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateAndExpression(left, right);
}
}
private sealed class OrElseTranslator : BinaryTranslator
{
internal OrElseTranslator()
: base(ExpressionType.OrElse) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateOrExpression(left, right);
}
}
private sealed class LessThanTranslator : BinaryTranslator
{
internal LessThanTranslator()
: base(ExpressionType.LessThan) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateLessThanExpression(left, right);
}
}
private sealed class LessThanOrEqualsTranslator : BinaryTranslator
{
internal LessThanOrEqualsTranslator()
: base(ExpressionType.LessThanOrEqual) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateLessThanOrEqualsExpression(left, right);
}
}
private sealed class GreaterThanTranslator : BinaryTranslator
{
internal GreaterThanTranslator()
: base(ExpressionType.GreaterThan) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateGreaterThanExpression(left, right);
}
}
private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator
{
internal GreaterThanOrEqualsTranslator()
: base(ExpressionType.GreaterThanOrEqual) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateGreaterThanOrEqualsExpression(left, right);
}
}
private sealed class EqualsTranslator : TypedTranslator
{
internal EqualsTranslator()
: base(ExpressionType.Equal) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
Expression linqLeft = linq.Left;
Expression linqRight = linq.Right;
bool leftIsNull = LinqExpressionIsNullConstant(linqLeft);
bool rightIsNull = LinqExpressionIsNullConstant(linqRight);
// if both values are null, short-circuit
if (leftIsNull && rightIsNull) { return parent._commandTree.CreateTrueExpression(); }
// if only one side is null, produce an IsNull statement
if (leftIsNull) { return CreateIsNullExpression(parent, linqRight); }
if (rightIsNull) { return CreateIsNullExpression(parent, linqLeft); }
// create a standard equals expression, calling utility method to compensate for null equality
CqtExpression cqtLeft = parent.TranslateExpression(linqLeft);
CqtExpression cqtRight = parent.TranslateExpression(linqRight);
return parent.CreateEqualsExpression(cqtLeft, cqtRight, EqualsPattern.Store, linqLeft.Type, linqRight.Type);
}
private static CqtExpression CreateIsNullExpression(ExpressionConverter parent, LinqExpression input)
{
input = UnwrapConvert(input);
// translate input
CqtExpression inputCqt = parent.TranslateExpression(input);
// create IsNull expression
return parent.CreateIsNullExpression(inputCqt, input.Type);
}
private static bool LinqExpressionIsNullConstant(LinqExpression expression)
{
// convert statements introduced by compiler should not affect nullness
expression = UnwrapConvert(expression);
// check if the unwrapped expression is a null constant
if (ExpressionType.Constant != expression.NodeType) { return false; }
System.Linq.Expressions.ConstantExpression constant = (System.Linq.Expressions.ConstantExpression)expression;
return null == constant.Value;
}
private static LinqExpression UnwrapConvert(LinqExpression input)
{
// unwrap all converts
while (ExpressionType.Convert == input.NodeType)
{
input = ((UnaryExpression)input).Operand;
}
return input;
}
}
private sealed class NotEqualsTranslator : TypedTranslator
{
internal NotEqualsTranslator()
: base(ExpressionType.NotEqual) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
// rewrite as a not equals expression
LinqExpression notLinq = LinqExpression.Not(
LinqExpression.Equal(linq.Left, linq.Right));
return parent.TranslateExpression(notLinq);
}
}
#endregion
#region Type binary expression translator
private sealed class IsTranslator : TypedTranslator
{
internal IsTranslator()
: base(ExpressionType.TypeIs) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq)
{
CqtExpression operand = parent.TranslateExpression(linq.Expression);
TypeUsage fromType = operand.ResultType;
TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type);
return parent._commandTree.CreateIsOfExpression(operand, toType);
}
}
#endregion
#region Arithmetic expressions
private sealed class AddTranslator : BinaryTranslator
{
internal AddTranslator()
: base(ExpressionType.Add, ExpressionType.AddChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
if (TypeSemantics.IsPrimitiveType(left.ResultType, PrimitiveTypeKind.String) &&
TypeSemantics.IsPrimitiveType(right.ResultType, PrimitiveTypeKind.String))
{
// Add(string, string) => Concat(string, string)
return parent.CreateCanonicalFunction(ExpressionConverter.Concat, linq, left, right);
}
else
{
return parent._commandTree.CreatePlusExpression(left, right);
}
}
}
private sealed class DivideTranslator : BinaryTranslator
{
internal DivideTranslator()
: base(ExpressionType.Divide) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateDivideExpression(left, right);
}
}
private sealed class ModuloTranslator : BinaryTranslator
{
internal ModuloTranslator()
: base(ExpressionType.Modulo) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateModuloExpression(left, right);
}
}
private sealed class MultiplyTranslator : BinaryTranslator
{
internal MultiplyTranslator()
: base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateMultiplyExpression(left, right);
}
}
private sealed class SubtractTranslator : BinaryTranslator
{
internal SubtractTranslator()
: base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateMinusExpression(left, right);
}
}
private sealed class NegateTranslator : UnaryTranslator
{
internal NegateTranslator()
: base(ExpressionType.Negate, ExpressionType.NegateChecked) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
return parent._commandTree.CreateUnaryMinusExpression(operand);
}
}
private sealed class UnaryPlusTranslator : UnaryTranslator
{
internal UnaryPlusTranslator()
: base(ExpressionType.UnaryPlus) { }
protected override DbExpression TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)
{
// +x = x
return operand;
}
}
#endregion
#region Bitwise expressions
private abstract class BitwiseBinaryTranslator : TypedTranslator
{
private readonly string _canonicalFunctionName;
protected BitwiseBinaryTranslator(ExpressionType nodeType, string canonicalFunctionName)
: base(nodeType)
{
_canonicalFunctionName = canonicalFunctionName;
}
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
CqtExpression left = parent.TranslateExpression(linq.Left);
CqtExpression right = parent.TranslateExpression(linq.Right);
//If the arguments are binary we translate into logic expressions
if (TypeSemantics.IsBooleanType(left.ResultType))
{
return TranslateIntoLogicExpression(parent, linq, left, right);
}
//Otherwise we translate into bitwise canonical functions
return parent.CreateCanonicalFunction(_canonicalFunctionName, linq, left, right);
}
protected abstract CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right);
}
private sealed class AndTranslator : BitwiseBinaryTranslator
{
internal AndTranslator()
: base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
return parent._commandTree.CreateAndExpression(left, right);
}
}
private sealed class OrTranslator : BitwiseBinaryTranslator
{
internal OrTranslator()
: base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
return parent._commandTree.CreateOrExpression(left, right);
}
}
private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator
{
internal ExclusiveOrTranslator()
: base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
//No direct translation, we translate into ((left && !right) || (!left && right))
CqtExpression firstExpression = parent._commandTree.CreateAndExpression(
left,
parent._commandTree.CreateNotExpression(right));
CqtExpression secondExpression = parent._commandTree.CreateAndExpression(
parent._commandTree.CreateNotExpression(parent.TranslateExpression(linq.Left)),
parent.TranslateExpression(linq.Right));
CqtExpression result = parent._commandTree.CreateOrExpression(firstExpression, secondExpression);
return result;
}
}
private sealed class NotTranslator : TypedTranslator
{
internal NotTranslator()
: base(ExpressionType.Not) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
{
CqtExpression operand = parent.TranslateExpression(linq.Operand);
if (TypeSemantics.IsBooleanType(operand.ResultType))
{
return parent._commandTree.CreateNotExpression(operand);
}
return parent.CreateCanonicalFunction(ExpressionConverter.BitwiseNot, linq, operand);
}
}
#endregion
#region Unary expression translators
private abstract class UnaryTranslator
: TypedTranslator
{
protected UnaryTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
{
return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand));
}
protected abstract CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand);
}
private sealed class QuoteTranslator : UnaryTranslator
{
internal QuoteTranslator()
: base(ExpressionType.Quote) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
// simply return the operand: expressions compilations not cached for LINQ, so
// parameters are always bound properly
return operand;
}
}
private sealed class ConvertTranslator : UnaryTranslator
{
internal ConvertTranslator()
: base(ExpressionType.Convert, ExpressionType.ConvertChecked) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
Type toClrType = unary.Type;
Type fromClrType = unary.Operand.Type;
CqtExpression source = parent.TranslateExpression(unary.Operand);
return parent.CreateCastExpression(source, toClrType, fromClrType);
}
}
private sealed class AsTranslator : UnaryTranslator
{
internal AsTranslator()
: base(ExpressionType.TypeAs) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
TypeUsage fromType = operand.ResultType;
TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type);
return parent._commandTree.CreateTreatExpression(operand, toType);
}
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....], [....]
//---------------------------------------------------------------------
using System.Data.Common.CommandTrees;
using System.Collections.Generic;
using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
using LinqExpression = System.Linq.Expressions.Expression;
using System.Diagnostics;
using System.Data.Metadata.Edm;
using System.Linq.Expressions;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity;
namespace System.Data.Objects.ELinq
{
internal sealed partial class ExpressionConverter
{
// Base class supporting the translation of LINQ node type(s) given a LINQ expression
// of that type, and the "parent" translation context (the ExpressionConverter processor)
private abstract class Translator
{
private readonly ExpressionType[] _nodeTypes;
protected Translator(params ExpressionType[] nodeTypes)
{
_nodeTypes = nodeTypes;
}
// Gets LINQ node types this translator should be registed to process.
internal IEnumerable NodeTypes { get { return _nodeTypes; } }
internal abstract CqtExpression Translate(ExpressionConverter parent, LinqExpression linq);
public override string ToString()
{
return this.GetType().Name;
}
}
#region Misc
// Typed version of Translator
private abstract class TypedTranslator : Translator
where T_Linq : LinqExpression
{
protected TypedTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq)
{
return TypedTranslate(parent, (T_Linq)linq);
}
protected abstract CqtExpression TypedTranslate(ExpressionConverter parent, T_Linq linq);
}
private sealed class ConstantTranslator
: TypedTranslator
{
internal ConstantTranslator()
: base(ExpressionType.Constant) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.ConstantExpression linq)
{
ObjectQuery queryOfT = linq.Value as ObjectQuery;
if (null != queryOfT)
{
return parent.TranslateInlineQueryOfT(queryOfT);
}
bool isNullValue = null == linq.Value;
// Remove facet information: null instances do not constrain type facets (e.g. a null string does not restrict
// "length" in compatibility checks)
TypeUsage type;
bool typeSupported = false;
if (parent.TryGetValueLayerType(linq.Type, out type))
{
// For constant values, support only primitive type (this is all that is supported by CQTs)
// For null types, also allow EntityType. Although other types claim to be supported, they
// don't work (e.g. complex type, see SQL BU 543956)
if (type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType ||
(isNullValue && type.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType))
{
typeSupported = true;
}
}
if (!typeSupported)
{
throw EntityUtil.NotSupported(Strings.ELinq_UnsupportedConstant(DescribeClrType(linq.Type), Strings.ELinq_PrimitiveTypesSample));
}
// create a constant or null expression depending on value
if (isNullValue)
{
return parent._commandTree.CreateNullExpression(type);
}
else
{
return parent._commandTree.CreateConstantExpression(linq.Value, type);
}
}
}
private sealed class MemberAccessTranslator
: TypedTranslator
{
internal MemberAccessTranslator()
: base(ExpressionType.MemberAccess) { }
// attempt to translate the member access to a "regular" property, a navigation property, or a calculated
// property
protected override CqtExpression TypedTranslate(ExpressionConverter parent, MemberExpression linq)
{
CqtExpression propertyExpression;
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Member, out memberName, out memberType);
// note: we check for "regular" properties last, since the other two flavors derive
// from this one
if (linq.Expression != null)
{
CqtExpression instance = parent.TranslateExpression(linq.Expression);
if (TryResolveAsProperty(parent, memberInfo,
instance.ResultType, instance, out propertyExpression))
{
return propertyExpression;
}
}
if (memberInfo.MemberType == MemberTypes.Property)
{
// Check whether it is one of the special proeperties that we know how to translate
PropertyTranslator propertyTranslator;
if (TryGetTranslator((PropertyInfo)memberInfo, out propertyTranslator))
{
return propertyTranslator.Translate(parent, linq);
}
}
// no other property types are supported by LINQ over entities
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnrecognizedMember(linq.Member.Name));
}
#region Static members and initializers
private static readonly Dictionary s_propertyTranslators;
private static bool s_vbPropertiesInitialized;
private static readonly object s_vbInitializerLock = new object();
static MemberAccessTranslator()
{
// initialize translators for specific properties
s_propertyTranslators = new Dictionary();
foreach (PropertyTranslator translator in GetPropertyTranslators())
{
foreach (PropertyInfo property in translator.Properties)
{
s_propertyTranslators.Add(property, translator);
}
}
}
///
/// Tries to get a translator for the given property info.
/// If the given property info corresponds to a Visual Basic property,
/// it also initializes the Visual Basic translators if they have not been initialized
///
///
///
///
private static bool TryGetTranslator(PropertyInfo propertyInfo, out PropertyTranslator propertyTranslator)
{
//If the type is generic, we try to match the generic property
if (propertyInfo.DeclaringType.IsGenericType)
{
try
{
propertyInfo = propertyInfo.DeclaringType.GetGenericTypeDefinition().GetProperty(propertyInfo.Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
}
catch (AmbiguousMatchException)
{
propertyTranslator = null;
return false;
}
if (propertyInfo == null)
{
propertyTranslator = null;
return false;
}
}
if (s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator))
{
return true;
}
// check if this is the visual basic assembly
if (s_visualBasicAssemblyFullName == propertyInfo.DeclaringType.Assembly.FullName)
{
lock (s_vbInitializerLock)
{
if (!s_vbPropertiesInitialized)
{
InitializeVBProperties(propertyInfo.DeclaringType.Assembly);
s_vbPropertiesInitialized = true;
}
// try again
return s_propertyTranslators.TryGetValue(propertyInfo, out propertyTranslator);
}
}
propertyTranslator = null;
return false;
}
// Determines if the given property can be resolved as a standard or navigation property.
private static bool TryResolveAsProperty(ExpressionConverter parent,
MemberInfo clrMember, TypeUsage definingType, CqtExpression instance, out CqtExpression propertyExpression)
{
// retrieve members directly from row types, which are not mapped between O and C
RowType rowType = definingType.EdmType as RowType;
string name = clrMember.Name;
if (null != rowType)
{
EdmMember member;
if (rowType.Members.TryGetValue(name, false, out member))
{
propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance);
return true;
}
propertyExpression = null;
return false;
}
// for non-row structural types, map from the O to the C layer using the perspective
StructuralType structuralType = definingType.EdmType as StructuralType;
if (null != structuralType)
{
EdmMember member = null;
if (parent._typeResolver.Perspective.TryGetMember(structuralType, name, false, out member))
{
if (null != member)
{
if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
{
NavigationProperty navProp = (NavigationProperty)member;
propertyExpression = TranslateNavigationProperty(parent, clrMember, instance, navProp);
return true;
}
else
{
// if we're accessing a key member of a navigation, collapse the structure instance
// to the key reference.
instance = NormalizeInstanceForReference(parent, instance, member);
propertyExpression = parent._commandTree.CreatePropertyExpression(name, instance);
return true;
}
}
}
}
// try to unwrap GroupBy "Key" member
if (name == ExpressionConverter.KeyColumnName)
{
// see if we can "unwrap" the current instance
if (DbExpressionKind.Property == instance.ExpressionKind)
{
DbPropertyExpression property = (DbPropertyExpression)instance;
InitializerMetadata initializerMetadata;
// if we're dealing with the "Group" property of a GroupBy projection, we know how to unwrap
// it
if (property.Property.Name == ExpressionConverter.GroupColumnName && // only know how to unwrap the group
InitializerMetadata.TryGetInitializerMetadata(property.Instance.ResultType, out initializerMetadata) &&
initializerMetadata.Kind == InitializerMetadataKind.Grouping)
{
propertyExpression = parent._commandTree.CreatePropertyExpression(ExpressionConverter.KeyColumnName,
property.Instance);
return true;
}
}
}
propertyExpression = null;
return false;
}
///
/// We simplify the property instance where the user is accessing a key member of
/// a reference navigation. The instance becomes simply the reference key in such
/// cases.
///
/// For instance, product.Category.CategoryID becomes Ref(product.Category).CategoryID,
/// which gives us a chance of optimizing the query (using foreign keys rather than joins)
///
/// Conversion context.
/// Instance from which we're accessing member.
/// Member we're accessing.
/// 'Simplified' instance. If the member is a key and the instance is a navigation
/// the returned instance is a reference navigation rather than the full entity.
private static CqtExpression NormalizeInstanceForReference(ExpressionConverter parent, CqtExpression instance, EdmMember member)
{
EdmType instanceType = instance.ResultType.EdmType;
if (instanceType.BuiltInTypeKind == BuiltInTypeKind.EntityType)
{
EntityType entityType = (EntityType)instanceType;
if (entityType.KeyMembers.Contains(member) && instance.ExpressionKind == DbExpressionKind.Property)
{
DbPropertyExpression propertyExpression = (DbPropertyExpression)instance;
if (propertyExpression.Property.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
{
// modify the property expression so that it merely retrieves the reference
// not the entire entity
NavigationProperty navigationProperty = (NavigationProperty)propertyExpression.Property;
DbExpression navigationSource = parent._commandTree.CreateEntityRefExpression(propertyExpression.Instance);
DbExpression navigationExpression = parent._commandTree.CreateRelationshipNavigationExpression(
navigationProperty.FromEndMember,
navigationProperty.ToEndMember,
navigationSource);
instance = parent._commandTree.CreateRefKeyExpression(navigationExpression);
}
}
}
return instance;
}
private static CqtExpression TranslateNavigationProperty(ExpressionConverter parent, MemberInfo clrMember, CqtExpression instance, NavigationProperty navProp)
{
CqtExpression propertyExpression;
propertyExpression = parent._commandTree.CreatePropertyExpression(navProp, instance);
// for EntityCollection navigations, wrap in "grouping" where the key is the parent
// entity and the group contains the child entities
if (BuiltInTypeKind.CollectionType == propertyExpression.ResultType.EdmType.BuiltInTypeKind)
{
List> collectionColumns =
new List>(2);
collectionColumns.Add(new KeyValuePair(
ExpressionConverter.EntityCollectionOwnerColumnName, instance));
collectionColumns.Add(new KeyValuePair(
ExpressionConverter.EntityCollectionElementsColumnName, propertyExpression));
propertyExpression = parent.CreateNewRowExpression(collectionColumns,
InitializerMetadata.CreateEntityCollectionInitializer(parent.EdmItemCollection, ((PropertyInfo)clrMember).PropertyType, navProp));
}
return propertyExpression;
}
private static void InitializeVBProperties(Assembly vbAssembly)
{
Debug.Assert(!s_vbPropertiesInitialized);
foreach (PropertyTranslator translator in GetVisualBasicPropertyTranslators(vbAssembly))
{
foreach (PropertyInfo property in translator.Properties)
{
s_propertyTranslators.Add(property, translator);
}
}
}
private static IEnumerable GetVisualBasicPropertyTranslators(Assembly vbAssembly)
{
yield return new VBDateAndTimeNowTranslator(vbAssembly);
}
private static IEnumerable GetPropertyTranslators()
{
yield return new DefaultCanonicalFunctionPropertyTranslator();
yield return new RenameCanonicalFunctionPropertyTranslator();
yield return new EntityCollectionCountTranslator();
yield return new NullableHasValueTranslator();
yield return new NullableValueTranslator();
}
///
/// Checks whether the given property info can be translated into a canonical function.
/// This method is used to determine whether client side evaluation should be done,
/// if the property can be evaluated in the store, it is not being evaluated on the client
///
///
///
internal static bool CanTranslatePropertyInfo(PropertyInfo propertyInfo)
{
PropertyTranslator propertyTranslator;
return TryGetTranslator(propertyInfo, out propertyTranslator);
}
#endregion
#region Property Translators
private abstract class PropertyTranslator
{
private readonly IEnumerable _properties;
protected PropertyTranslator(params PropertyInfo[] properties) { _properties = properties; }
protected PropertyTranslator(IEnumerable properties) { _properties = properties; }
internal IEnumerable Properties { get { return _properties; } }
internal abstract CqtExpression Translate(ExpressionConverter parent, MemberExpression call);
public override string ToString()
{
return GetType().Name;
}
}
private sealed class DefaultCanonicalFunctionPropertyTranslator : PropertyTranslator
{
internal DefaultCanonicalFunctionPropertyTranslator()
: base(GetProperties()) { }
private static IEnumerable GetProperties()
{
yield return typeof(String).GetProperty("Length", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTime).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Year", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Month", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Day", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Hour", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Minute", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Second", BindingFlags.Public | BindingFlags.Instance);
yield return typeof(DateTimeOffset).GetProperty("Millisecond", BindingFlags.Public | BindingFlags.Instance);
}
// Default translator for method calls into canonical functions.
// Translation:
// object.PropertyName -> PropertyName(object)
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
return parent.TranslateIntoCanonicalFunction(call.Member.Name, call, call.Expression);
}
}
private sealed class RenameCanonicalFunctionPropertyTranslator : PropertyTranslator
{
private static readonly Dictionary s_propertyRenameMap = new Dictionary(2);
internal RenameCanonicalFunctionPropertyTranslator()
: base(GetProperties()) { }
private static IEnumerable GetProperties()
{
yield return GetProperty(typeof(DateTime), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTime);
yield return GetProperty(typeof(DateTime), "UtcNow", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentUtcDateTime);
yield return GetProperty(typeof(DateTimeOffset), "Now", BindingFlags.Public | BindingFlags.Static, ExpressionConverter.CurrentDateTimeOffset);
yield return GetProperty(typeof(TimeSpan), "Hours", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Hour);
yield return GetProperty(typeof(TimeSpan), "Minutes", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Minute);
yield return GetProperty(typeof(TimeSpan), "Seconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Second);
yield return GetProperty(typeof(TimeSpan), "Milliseconds", BindingFlags.Public | BindingFlags.Instance, ExpressionConverter.Millisecond);
}
private static PropertyInfo GetProperty(Type declaringType, string propertyName, BindingFlags bindingFlages, string canonicalFunctionName)
{
PropertyInfo propertyInfo = declaringType.GetProperty(propertyName, bindingFlages);
s_propertyRenameMap.Add(propertyInfo, canonicalFunctionName);
return propertyInfo;
}
// Translator for static properties into canonical functions when there is a corresponding
// canonical function but with a differnet name
// Translation:
// object.PropertyName -> CanonicalFunctionName(object)
// Type.PropertyName -> CanonicalFunctionName()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
PropertyInfo property = (PropertyInfo)call.Member;
String canonicalFunctionName = s_propertyRenameMap[property];
CqtExpression result;
if (call.Expression == null)
{
result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call);
}
else
{
result = parent.TranslateIntoCanonicalFunction(canonicalFunctionName, call, call.Expression);
}
return result;
}
}
private sealed class VBDateAndTimeNowTranslator : PropertyTranslator
{
private const string s_dateAndTimeTypeFullName = "Microsoft.VisualBasic.DateAndTime";
internal VBDateAndTimeNowTranslator(Assembly vbAssembly)
: base(GetProperty(vbAssembly)) { }
private static PropertyInfo GetProperty(Assembly vbAssembly)
{
return vbAssembly.GetType(s_dateAndTimeTypeFullName).GetProperty("Now", BindingFlags.Public | BindingFlags.Static);
}
// Translation:
// Now -> GetDate()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
return parent.TranslateIntoCanonicalFunction(ExpressionConverter.CurrentDateTime, call);
}
}
private sealed class EntityCollectionCountTranslator : PropertyTranslator
{
internal EntityCollectionCountTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(EntityCollection<>).GetProperty(ExpressionConverter.s_entityCollectionCountPropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// EntityCollection.Count -> Count()
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
// retranslate as a Count() aggregate, since the name collision prevents us
// from calling the method directly in VB and C#
MethodInfo countMethod;
ReflectionUtil.TryLookupMethod(SequenceMethod.Count, out countMethod);
Debug.Assert(null != countMethod, "Count() must exist");
countMethod = countMethod.MakeGenericMethod(call.Member.DeclaringType.GetGenericArguments());
LinqExpression countCall = LinqExpression.Call(countMethod, call.Expression);
return parent.TranslateExpression(countCall);
}
}
private sealed class NullableHasValueTranslator : PropertyTranslator
{
internal NullableHasValueTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableHasValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// Nullable.HasValue -> Not(IsNull(arg))
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
CqtExpression argument = parent.TranslateExpression(call.Expression);
Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
return parent._commandTree.CreateNotExpression(parent.CreateIsNullExpression(argument, call.Expression.Type));
}
}
private sealed class NullableValueTranslator : PropertyTranslator
{
internal NullableValueTranslator()
: base(GetProperty()) { }
private static PropertyInfo GetProperty()
{
return typeof(Nullable<>).GetProperty(ExpressionConverter.s_nullableValuePropertyName, BindingFlags.Public | BindingFlags.Instance);
}
// Translation:
// Nullable.Value -> arg
internal override CqtExpression Translate(ExpressionConverter parent, MemberExpression call)
{
CqtExpression argument = parent.TranslateExpression(call.Expression);
Debug.Assert(!TypeSemantics.IsCollectionType(argument.ResultType), "Did not expect collection type");
return argument;
}
}
#endregion
}
private sealed class ParameterTranslator
: TypedTranslator
{
internal ParameterTranslator()
: base(ExpressionType.Parameter) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, ParameterExpression linq)
{
// Bindings should be intercepted before we get to this point (in ExpressionConverter.TranslateExpression)
if (parent._bindingContext.IsRootContextParameter(linq))
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnsupportedUseOfContextParameter(linq.Name));
}
else
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnboundParameterExpression(linq.Name));
}
}
}
private sealed class NewTranslator
: TypedTranslator
{
internal NewTranslator()
: base(ExpressionType.New) { }
protected override DbExpression TypedTranslate(ExpressionConverter parent, NewExpression linq)
{
int memberCount = null == linq.Members ? 0 : linq.Members.Count;
if (null == linq.Constructor ||
linq.Arguments.Count != memberCount)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
}
parent.CheckInitializerType(linq.Type);
List> recordColumns =
new List>(memberCount + 1);
HashSet memberNames = new HashSet(StringComparer.Ordinal);
for (int i = 0; i < memberCount; i++)
{
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(linq.Members[i], out memberName, out memberType);
CqtExpression memberValue = parent.TranslateExpression(linq.Arguments[i]);
memberNames.Add(memberName);
recordColumns.Add(new KeyValuePair(memberName, memberValue));
}
InitializerMetadata initializerMetadata;
if (0 == memberCount)
{
// add a sentinel column because CQTs do not accept empty row types
recordColumns.Add(new KeyValuePair(KeyColumnName, parent._commandTree.CreateTrueExpression()));
initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq);
}
else
{
// Construct a new initializer type in metadata for this projection (provides the
// necessary context for the object materializer)
initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq);
}
parent.ValidateInitializerMetadata(initializerMetadata);
DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
return projection;
}
}
private sealed class MemberInitTranslator
: TypedTranslator
{
internal MemberInitTranslator()
: base(ExpressionType.MemberInit) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
{
if (null == linq.NewExpression.Constructor ||
0 != linq.NewExpression.Constructor.GetParameters().Length)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedConstructor);
}
parent.CheckInitializerType(linq.Type);
List> recordColumns =
new List>(linq.Bindings.Count + 1);
MemberInfo[] members = new MemberInfo[linq.Bindings.Count];
HashSet memberNames = new HashSet(StringComparer.Ordinal);
for (int i = 0; i < linq.Bindings.Count; i++)
{
MemberAssignment binding = linq.Bindings[i] as MemberAssignment;
if (null == binding)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedBinding);
}
string memberName;
Type memberType;
MemberInfo memberInfo = TypeSystem.PropertyOrField(binding.Member, out memberName, out memberType);
CqtExpression memberValue = parent.TranslateExpression(binding.Expression);
memberNames.Add(memberName);
members[i] = memberInfo;
recordColumns.Add(new KeyValuePair(memberName, memberValue));
}
InitializerMetadata initializerMetadata;
if (0 == recordColumns.Count)
{
// add a sentinel column because CQTs do not accept empty row types
recordColumns.Add(new KeyValuePair(KeyColumnName, parent._commandTree.CreateTrueExpression()));
initializerMetadata = InitializerMetadata.CreateEmptyProjectionInitializer(parent.EdmItemCollection, linq.NewExpression);
}
else
{
// Construct a new initializer type in metadata for this projection (provides the
// necessary context for the object materializer)
initializerMetadata = InitializerMetadata.CreateProjectionInitializer(parent.EdmItemCollection, linq, members);
}
parent.ValidateInitializerMetadata(initializerMetadata);
DbNewInstanceExpression projection = parent.CreateNewRowExpression(recordColumns, initializerMetadata);
return projection;
}
}
private sealed class ConditionalTranslator : TypedTranslator
{
internal ConditionalTranslator()
: base(ExpressionType.Conditional) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, ConditionalExpression linq)
{
// translate Test ? IfTrue : IfFalse --> CASE WHEN Test THEN IfTrue ELSE IfFalse
List whenExpressions = new List(1);
whenExpressions.Add(parent.TranslateExpression(linq.Test));
List thenExpressions = new List(1);
thenExpressions.Add(parent.TranslateExpression(linq.IfTrue));
CqtExpression elseExpression = parent.TranslateExpression(linq.IfFalse);
return parent._commandTree.CreateCaseExpression(whenExpressions, thenExpressions, elseExpression);
}
}
private sealed class NotSupportedTranslator : Translator
{
internal NotSupportedTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes)
{
}
internal override CqtExpression Translate(ExpressionConverter parent, LinqExpression linq)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedExpressionType(linq.NodeType));
}
}
#endregion
#region Binary expression translators
private abstract class BinaryTranslator
: TypedTranslator
{
protected BinaryTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
return TranslateBinary(parent, parent.TranslateExpression(linq.Left), parent.TranslateExpression(linq.Right), linq);
}
protected abstract CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq);
}
private sealed class CoalesceTranslator : BinaryTranslator
{
internal CoalesceTranslator()
: base(ExpressionType.Coalesce) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
// left ?? right gets translated to:
// CASE WHEN IsNull(left) THEN right ELSE left
// construct IsNull
CqtExpression isNull = parent.CreateIsNullExpression(left, linq.Left.Type);
// construct case expression
List whenExpressions = new List(1);
whenExpressions.Add(isNull);
List thenExpressions = new List(1);
thenExpressions.Add(right);
CqtExpression caseExpression = parent._commandTree.CreateCaseExpression(whenExpressions,
thenExpressions, left);
return caseExpression;
}
}
private sealed class AndAlsoTranslator : BinaryTranslator
{
internal AndAlsoTranslator()
: base(ExpressionType.AndAlso) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateAndExpression(left, right);
}
}
private sealed class OrElseTranslator : BinaryTranslator
{
internal OrElseTranslator()
: base(ExpressionType.OrElse) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateOrExpression(left, right);
}
}
private sealed class LessThanTranslator : BinaryTranslator
{
internal LessThanTranslator()
: base(ExpressionType.LessThan) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateLessThanExpression(left, right);
}
}
private sealed class LessThanOrEqualsTranslator : BinaryTranslator
{
internal LessThanOrEqualsTranslator()
: base(ExpressionType.LessThanOrEqual) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateLessThanOrEqualsExpression(left, right);
}
}
private sealed class GreaterThanTranslator : BinaryTranslator
{
internal GreaterThanTranslator()
: base(ExpressionType.GreaterThan) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateGreaterThanExpression(left, right);
}
}
private sealed class GreaterThanOrEqualsTranslator : BinaryTranslator
{
internal GreaterThanOrEqualsTranslator()
: base(ExpressionType.GreaterThanOrEqual) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateGreaterThanOrEqualsExpression(left, right);
}
}
private sealed class EqualsTranslator : TypedTranslator
{
internal EqualsTranslator()
: base(ExpressionType.Equal) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
Expression linqLeft = linq.Left;
Expression linqRight = linq.Right;
bool leftIsNull = LinqExpressionIsNullConstant(linqLeft);
bool rightIsNull = LinqExpressionIsNullConstant(linqRight);
// if both values are null, short-circuit
if (leftIsNull && rightIsNull) { return parent._commandTree.CreateTrueExpression(); }
// if only one side is null, produce an IsNull statement
if (leftIsNull) { return CreateIsNullExpression(parent, linqRight); }
if (rightIsNull) { return CreateIsNullExpression(parent, linqLeft); }
// create a standard equals expression, calling utility method to compensate for null equality
CqtExpression cqtLeft = parent.TranslateExpression(linqLeft);
CqtExpression cqtRight = parent.TranslateExpression(linqRight);
return parent.CreateEqualsExpression(cqtLeft, cqtRight, EqualsPattern.Store, linqLeft.Type, linqRight.Type);
}
private static CqtExpression CreateIsNullExpression(ExpressionConverter parent, LinqExpression input)
{
input = UnwrapConvert(input);
// translate input
CqtExpression inputCqt = parent.TranslateExpression(input);
// create IsNull expression
return parent.CreateIsNullExpression(inputCqt, input.Type);
}
private static bool LinqExpressionIsNullConstant(LinqExpression expression)
{
// convert statements introduced by compiler should not affect nullness
expression = UnwrapConvert(expression);
// check if the unwrapped expression is a null constant
if (ExpressionType.Constant != expression.NodeType) { return false; }
System.Linq.Expressions.ConstantExpression constant = (System.Linq.Expressions.ConstantExpression)expression;
return null == constant.Value;
}
private static LinqExpression UnwrapConvert(LinqExpression input)
{
// unwrap all converts
while (ExpressionType.Convert == input.NodeType)
{
input = ((UnaryExpression)input).Operand;
}
return input;
}
}
private sealed class NotEqualsTranslator : TypedTranslator
{
internal NotEqualsTranslator()
: base(ExpressionType.NotEqual) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
// rewrite as a not equals expression
LinqExpression notLinq = LinqExpression.Not(
LinqExpression.Equal(linq.Left, linq.Right));
return parent.TranslateExpression(notLinq);
}
}
#endregion
#region Type binary expression translator
private sealed class IsTranslator : TypedTranslator
{
internal IsTranslator()
: base(ExpressionType.TypeIs) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, TypeBinaryExpression linq)
{
CqtExpression operand = parent.TranslateExpression(linq.Expression);
TypeUsage fromType = operand.ResultType;
TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeIs, linq.TypeOperand, linq.Expression.Type);
return parent._commandTree.CreateIsOfExpression(operand, toType);
}
}
#endregion
#region Arithmetic expressions
private sealed class AddTranslator : BinaryTranslator
{
internal AddTranslator()
: base(ExpressionType.Add, ExpressionType.AddChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
if (TypeSemantics.IsPrimitiveType(left.ResultType, PrimitiveTypeKind.String) &&
TypeSemantics.IsPrimitiveType(right.ResultType, PrimitiveTypeKind.String))
{
// Add(string, string) => Concat(string, string)
return parent.CreateCanonicalFunction(ExpressionConverter.Concat, linq, left, right);
}
else
{
return parent._commandTree.CreatePlusExpression(left, right);
}
}
}
private sealed class DivideTranslator : BinaryTranslator
{
internal DivideTranslator()
: base(ExpressionType.Divide) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateDivideExpression(left, right);
}
}
private sealed class ModuloTranslator : BinaryTranslator
{
internal ModuloTranslator()
: base(ExpressionType.Modulo) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateModuloExpression(left, right);
}
}
private sealed class MultiplyTranslator : BinaryTranslator
{
internal MultiplyTranslator()
: base(ExpressionType.Multiply, ExpressionType.MultiplyChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateMultiplyExpression(left, right);
}
}
private sealed class SubtractTranslator : BinaryTranslator
{
internal SubtractTranslator()
: base(ExpressionType.Subtract, ExpressionType.SubtractChecked) { }
protected override CqtExpression TranslateBinary(ExpressionConverter parent, CqtExpression left, CqtExpression right, BinaryExpression linq)
{
return parent._commandTree.CreateMinusExpression(left, right);
}
}
private sealed class NegateTranslator : UnaryTranslator
{
internal NegateTranslator()
: base(ExpressionType.Negate, ExpressionType.NegateChecked) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
return parent._commandTree.CreateUnaryMinusExpression(operand);
}
}
private sealed class UnaryPlusTranslator : UnaryTranslator
{
internal UnaryPlusTranslator()
: base(ExpressionType.UnaryPlus) { }
protected override DbExpression TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)
{
// +x = x
return operand;
}
}
#endregion
#region Bitwise expressions
private abstract class BitwiseBinaryTranslator : TypedTranslator
{
private readonly string _canonicalFunctionName;
protected BitwiseBinaryTranslator(ExpressionType nodeType, string canonicalFunctionName)
: base(nodeType)
{
_canonicalFunctionName = canonicalFunctionName;
}
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq)
{
CqtExpression left = parent.TranslateExpression(linq.Left);
CqtExpression right = parent.TranslateExpression(linq.Right);
//If the arguments are binary we translate into logic expressions
if (TypeSemantics.IsBooleanType(left.ResultType))
{
return TranslateIntoLogicExpression(parent, linq, left, right);
}
//Otherwise we translate into bitwise canonical functions
return parent.CreateCanonicalFunction(_canonicalFunctionName, linq, left, right);
}
protected abstract CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right);
}
private sealed class AndTranslator : BitwiseBinaryTranslator
{
internal AndTranslator()
: base(ExpressionType.And, ExpressionConverter.BitwiseAnd) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
return parent._commandTree.CreateAndExpression(left, right);
}
}
private sealed class OrTranslator : BitwiseBinaryTranslator
{
internal OrTranslator()
: base(ExpressionType.Or, ExpressionConverter.BitwiseOr) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
return parent._commandTree.CreateOrExpression(left, right);
}
}
private sealed class ExclusiveOrTranslator : BitwiseBinaryTranslator
{
internal ExclusiveOrTranslator()
: base(ExpressionType.ExclusiveOr, ExpressionConverter.BitwiseXor) { }
protected override CqtExpression TranslateIntoLogicExpression(ExpressionConverter parent, System.Linq.Expressions.BinaryExpression linq, CqtExpression left, CqtExpression right)
{
//No direct translation, we translate into ((left && !right) || (!left && right))
CqtExpression firstExpression = parent._commandTree.CreateAndExpression(
left,
parent._commandTree.CreateNotExpression(right));
CqtExpression secondExpression = parent._commandTree.CreateAndExpression(
parent._commandTree.CreateNotExpression(parent.TranslateExpression(linq.Left)),
parent.TranslateExpression(linq.Right));
CqtExpression result = parent._commandTree.CreateOrExpression(firstExpression, secondExpression);
return result;
}
}
private sealed class NotTranslator : TypedTranslator
{
internal NotTranslator()
: base(ExpressionType.Not) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
{
CqtExpression operand = parent.TranslateExpression(linq.Operand);
if (TypeSemantics.IsBooleanType(operand.ResultType))
{
return parent._commandTree.CreateNotExpression(operand);
}
return parent.CreateCanonicalFunction(ExpressionConverter.BitwiseNot, linq, operand);
}
}
#endregion
#region Unary expression translators
private abstract class UnaryTranslator
: TypedTranslator
{
protected UnaryTranslator(params ExpressionType[] nodeTypes)
: base(nodeTypes) { }
protected override CqtExpression TypedTranslate(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression linq)
{
return TranslateUnary(parent, linq, parent.TranslateExpression(linq.Operand));
}
protected abstract CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand);
}
private sealed class QuoteTranslator : UnaryTranslator
{
internal QuoteTranslator()
: base(ExpressionType.Quote) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
// simply return the operand: expressions compilations not cached for LINQ, so
// parameters are always bound properly
return operand;
}
}
private sealed class ConvertTranslator : UnaryTranslator
{
internal ConvertTranslator()
: base(ExpressionType.Convert, ExpressionType.ConvertChecked) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
Type toClrType = unary.Type;
Type fromClrType = unary.Operand.Type;
CqtExpression source = parent.TranslateExpression(unary.Operand);
return parent.CreateCastExpression(source, toClrType, fromClrType);
}
}
private sealed class AsTranslator : UnaryTranslator
{
internal AsTranslator()
: base(ExpressionType.TypeAs) { }
protected override CqtExpression TranslateUnary(ExpressionConverter parent, System.Linq.Expressions.UnaryExpression unary, CqtExpression operand)
{
TypeUsage fromType = operand.ResultType;
TypeUsage toType = parent.GetIsOrAsTargetType(fromType, ExpressionType.TypeAs, unary.Type, unary.Operand.Type);
return parent._commandTree.CreateTreatExpression(operand, toType);
}
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MetafileHeaderEmf.cs
- WindowsListViewGroup.cs
- SafeHandles.cs
- HuffCodec.cs
- sortedlist.cs
- InstalledFontCollection.cs
- FigureHelper.cs
- TextWriterTraceListener.cs
- SqlDataSourceFilteringEventArgs.cs
- SerializationObjectManager.cs
- HtmlUtf8RawTextWriter.cs
- EventArgs.cs
- DisplayInformation.cs
- WebColorConverter.cs
- X509ChainPolicy.cs
- WCFModelStrings.Designer.cs
- HwndHost.cs
- MouseActionConverter.cs
- DataGridViewCellPaintingEventArgs.cs
- ActiveXHelper.cs
- GeometryHitTestResult.cs
- PlainXmlWriter.cs
- XmlSchemaAppInfo.cs
- SafeNativeMethods.cs
- XmlUnspecifiedAttribute.cs
- PeerCredential.cs
- CodeTypeParameterCollection.cs
- FieldToken.cs
- VisualStyleElement.cs
- TableColumn.cs
- RSAPKCS1SignatureDeformatter.cs
- SchemaContext.cs
- GroupBoxRenderer.cs
- DebuggerAttributes.cs
- PropertyStore.cs
- EventSetterHandlerConverter.cs
- ClientUtils.cs
- Expression.cs
- GeneralTransform3DCollection.cs
- CatalogZone.cs
- DataSpaceManager.cs
- WinEventTracker.cs
- PrivateFontCollection.cs
- ToggleProviderWrapper.cs
- SourceElementsCollection.cs
- MultipartIdentifier.cs
- X509SecurityTokenProvider.cs
- TypeBuilder.cs
- XmlSiteMapProvider.cs
- CodeAttributeDeclaration.cs
- ProgressChangedEventArgs.cs
- RangeEnumerable.cs
- FormDocumentDesigner.cs
- HelpInfo.cs
- DesignerSerializerAttribute.cs
- CodeSnippetStatement.cs
- RegexFCD.cs
- CodeDirectionExpression.cs
- ProxyHwnd.cs
- HostedHttpTransportManager.cs
- SafeHandles.cs
- AncillaryOps.cs
- CategoryNameCollection.cs
- ShaderRenderModeValidation.cs
- Point3DCollection.cs
- HtmlWindow.cs
- CacheRequest.cs
- WpfSharedBamlSchemaContext.cs
- XmlTextEncoder.cs
- _LocalDataStore.cs
- ObjectDataSourceWizardForm.cs
- StringAnimationBase.cs
- TouchEventArgs.cs
- FixedTextContainer.cs
- RectangleGeometry.cs
- NotCondition.cs
- InteropExecutor.cs
- WebZoneDesigner.cs
- UnsignedPublishLicense.cs
- VectorAnimation.cs
- diagnosticsswitches.cs
- DispatcherHookEventArgs.cs
- activationcontext.cs
- BitStream.cs
- TreeViewDesigner.cs
- SrgsText.cs
- coordinatorscratchpad.cs
- CmsInterop.cs
- GridItemProviderWrapper.cs
- TextBlockAutomationPeer.cs
- CodeBinaryOperatorExpression.cs
- EnumValidator.cs
- ToolStripContentPanel.cs
- baseaxisquery.cs
- HiddenField.cs
- MD5.cs
- ConfigurationConverterBase.cs
- IApplicationTrustManager.cs
- TemplatePropertyEntry.cs
- XmlEncoding.cs