3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
713 lines
23 KiB
C#
713 lines
23 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="ExpressionKeyGen.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner [....]
|
|
// @backupOwner venkatja
|
|
//---------------------------------------------------------------------
|
|
|
|
namespace System.Data.Common.CommandTrees.Internal
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Data.Common.CommandTrees;
|
|
using System.Data.Common.Utils;
|
|
using System.Data.Metadata.Edm;
|
|
using System.Data.Spatial;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
/// <summary>
|
|
/// Generates a key for a command tree.
|
|
/// </summary>
|
|
internal sealed class ExpressionKeyGen : DbExpressionVisitor
|
|
{
|
|
internal static bool TryGenerateKey(DbExpression tree, out string key)
|
|
{
|
|
var keyGen = new ExpressionKeyGen();
|
|
try
|
|
{
|
|
tree.Accept(keyGen);
|
|
key = keyGen._key.ToString();
|
|
return true;
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
key = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private ExpressionKeyGen() { }
|
|
|
|
#region Fields
|
|
private readonly StringBuilder _key = new StringBuilder();
|
|
|
|
private static string[] _exprKindNames = InitializeExprKindNames();
|
|
private static string[] InitializeExprKindNames()
|
|
{
|
|
#if DEBUG
|
|
var values = Enum.GetValues(typeof(DbExpressionKind)).Cast<int>().ToArray();
|
|
for (int i = 0; i < values.Length; ++i)
|
|
{
|
|
// If there are gaps, then we need to change the algorithm for building _exprKindNames.
|
|
Debug.Assert(i == values[i], "Are there any gaps in DbExpressionKind members?");
|
|
}
|
|
#endif
|
|
var names = Enum.GetNames(typeof(DbExpressionKind));
|
|
|
|
// Arithmetic
|
|
names[(int)DbExpressionKind.Divide] = "/";
|
|
names[(int)DbExpressionKind.Modulo] = "%";
|
|
names[(int)DbExpressionKind.Multiply] = "*";
|
|
names[(int)DbExpressionKind.Plus] = "+";
|
|
names[(int)DbExpressionKind.Minus] = "-";
|
|
names[(int)DbExpressionKind.UnaryMinus] = "-";
|
|
|
|
// Comparison
|
|
names[(int)DbExpressionKind.Equals] = "=";
|
|
names[(int)DbExpressionKind.LessThan] = "<";
|
|
names[(int)DbExpressionKind.LessThanOrEquals] = "<=";
|
|
names[(int)DbExpressionKind.GreaterThan] = ">";
|
|
names[(int)DbExpressionKind.GreaterThanOrEquals] = ">=";
|
|
names[(int)DbExpressionKind.NotEquals] = "<>";
|
|
|
|
names[(int)DbExpressionKind.Property] = ".";
|
|
|
|
// Relops
|
|
names[(int)DbExpressionKind.InnerJoin] = "IJ";
|
|
names[(int)DbExpressionKind.FullOuterJoin] = "FOJ";
|
|
names[(int)DbExpressionKind.LeftOuterJoin] = "LOJ";
|
|
names[(int)DbExpressionKind.CrossApply] = "CA";
|
|
names[(int)DbExpressionKind.OuterApply] = "OA";
|
|
|
|
return names;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void VisitVariableName(string varName)
|
|
{
|
|
#if DEBUG
|
|
// There are generally four sources of var names:
|
|
// 1. generated by default alias generator (DbExpressionBuilder.AliasGenerator): "Var_123"
|
|
// 2. generated by ExpressionConverted.AliasGenerator (ELinq compiler): "LQ123"
|
|
// 3. generated by SemanticResolver.GenerateInternalName (eSQL compiler): "_##hint123"
|
|
// 4. inferred from user-defined artefacts, such as local names introduced inside a linq query
|
|
// Out of these four sources, ##2, 3 and 4 provide stable names in the sense that the same conversion by ExpressionConverted
|
|
// will produce the same variable names for the same linq query. It is assumed that unless there is a code defect,
|
|
// ELinq queries will contain variables from the stable sources only, so this check is debug only.
|
|
var _notSupportedVarNames = new Regex("^" + ExpressionBuilder.DbExpressionBuilder.AliasGenerator.Prefix + "[0-9]+");
|
|
Debug.Assert(_notSupportedVarNames.Match(varName).Success == false, "ExpressionKeyGen does not support variables generated using default expression builder alias generator.");
|
|
#endif
|
|
_key.Append('\'');
|
|
_key.Append(varName.Replace("'", "''"));
|
|
_key.Append('\'');
|
|
}
|
|
|
|
private void VisitBinding(DbExpressionBinding binding)
|
|
{
|
|
_key.Append("BV");
|
|
VisitVariableName(binding.VariableName);
|
|
_key.Append("=(");
|
|
binding.Expression.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitGroupBinding(DbGroupExpressionBinding groupBinding)
|
|
{
|
|
_key.Append("GBVV");
|
|
VisitVariableName(groupBinding.VariableName);
|
|
_key.Append(",");
|
|
VisitVariableName(groupBinding.GroupVariableName);
|
|
_key.Append("=(");
|
|
groupBinding.Expression.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitFunction(EdmFunction func, IList<DbExpression> args)
|
|
{
|
|
_key.Append("FUNC<");
|
|
_key.Append(func.Identity);
|
|
_key.Append(">:ARGS(");
|
|
foreach (var a in args)
|
|
{
|
|
_key.Append('(');
|
|
a.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitExprKind(DbExpressionKind kind)
|
|
{
|
|
_key.Append('[');
|
|
_key.Append(_exprKindNames[(int)kind]);
|
|
_key.Append(']');
|
|
}
|
|
|
|
private void VisitUnary(DbUnaryExpression expr)
|
|
{
|
|
VisitExprKind(expr.ExpressionKind);
|
|
_key.Append('(');
|
|
expr.Argument.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitBinary(DbBinaryExpression expr)
|
|
{
|
|
VisitExprKind(expr.ExpressionKind);
|
|
_key.Append('(');
|
|
expr.Left.Accept(this);
|
|
_key.Append(',');
|
|
expr.Right.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitCastOrTreat(DbUnaryExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
e.Argument.Accept(this);
|
|
_key.Append(":");
|
|
_key.Append(e.ResultType.Identity);
|
|
_key.Append(')');
|
|
}
|
|
|
|
#region DbExpressionVisitor Members
|
|
|
|
public override void Visit(DbExpression e)
|
|
{
|
|
throw EntityUtil.NotSupported(System.Data.Entity.Strings.Cqt_General_UnsupportedExpression(e.GetType().FullName));
|
|
}
|
|
|
|
public override void Visit(DbConstantExpression e)
|
|
{
|
|
Debug.Assert(TypeSemantics.IsScalarType(e.ResultType), "Non-scalar type constant expressions are not supported.");
|
|
var primitive = TypeHelpers.GetPrimitiveTypeUsageForScalar(e.ResultType);
|
|
|
|
switch (((PrimitiveType)primitive.EdmType).PrimitiveTypeKind)
|
|
{
|
|
case PrimitiveTypeKind.Binary:
|
|
var byteArray = e.Value as byte[];
|
|
if (byteArray != null)
|
|
{
|
|
_key.Append("'");
|
|
foreach (byte b in byteArray)
|
|
{
|
|
_key.AppendFormat("{0:X2}", b);
|
|
}
|
|
_key.Append("'");
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
break;
|
|
case PrimitiveTypeKind.String:
|
|
var @string = e.Value as string;
|
|
if (@string != null)
|
|
{
|
|
_key.Append("'");
|
|
_key.Append(@string.Replace("'", "''"));
|
|
_key.Append("'");
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
break;
|
|
|
|
case PrimitiveTypeKind.Boolean:
|
|
case PrimitiveTypeKind.Byte:
|
|
case PrimitiveTypeKind.Decimal:
|
|
case PrimitiveTypeKind.Double:
|
|
case PrimitiveTypeKind.Guid:
|
|
case PrimitiveTypeKind.Single:
|
|
case PrimitiveTypeKind.SByte:
|
|
case PrimitiveTypeKind.Int16:
|
|
case PrimitiveTypeKind.Int32:
|
|
case PrimitiveTypeKind.Int64:
|
|
case PrimitiveTypeKind.Time:
|
|
_key.AppendFormat(CultureInfo.InvariantCulture, "{0}", e.Value);
|
|
break;
|
|
|
|
case PrimitiveTypeKind.DateTime:
|
|
_key.Append(((DateTime)e.Value).ToString("o", CultureInfo.InvariantCulture));
|
|
break;
|
|
|
|
case PrimitiveTypeKind.DateTimeOffset:
|
|
_key.Append(((DateTimeOffset)e.Value).ToString("o", CultureInfo.InvariantCulture));
|
|
break;
|
|
|
|
case PrimitiveTypeKind.Geometry:
|
|
case PrimitiveTypeKind.GeometryPoint:
|
|
case PrimitiveTypeKind.GeometryLineString:
|
|
case PrimitiveTypeKind.GeometryPolygon:
|
|
case PrimitiveTypeKind.GeometryMultiPoint:
|
|
case PrimitiveTypeKind.GeometryMultiLineString:
|
|
case PrimitiveTypeKind.GeometryMultiPolygon:
|
|
case PrimitiveTypeKind.GeometryCollection:
|
|
var geometry = e.Value as DbGeometry;
|
|
if (geometry != null)
|
|
{
|
|
_key.Append(geometry.AsText());
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
break;
|
|
case PrimitiveTypeKind.Geography:
|
|
case PrimitiveTypeKind.GeographyPoint:
|
|
case PrimitiveTypeKind.GeographyLineString:
|
|
case PrimitiveTypeKind.GeographyPolygon:
|
|
case PrimitiveTypeKind.GeographyMultiPoint:
|
|
case PrimitiveTypeKind.GeographyMultiLineString:
|
|
case PrimitiveTypeKind.GeographyMultiPolygon:
|
|
case PrimitiveTypeKind.GeographyCollection:
|
|
var geography = e.Value as DbGeography;
|
|
if (geography != null)
|
|
{
|
|
_key.Append(geography.AsText());
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
_key.Append(":");
|
|
_key.Append(e.ResultType.Identity);
|
|
}
|
|
|
|
public override void Visit(DbNullExpression e)
|
|
{
|
|
_key.Append("NULL:");
|
|
_key.Append(e.ResultType.Identity);
|
|
}
|
|
|
|
public override void Visit(DbVariableReferenceExpression e)
|
|
{
|
|
_key.Append("Var(");
|
|
VisitVariableName(e.VariableName);
|
|
_key.Append(")");
|
|
}
|
|
|
|
public override void Visit(DbParameterReferenceExpression e)
|
|
{
|
|
_key.Append("@");
|
|
_key.Append(e.ParameterName);
|
|
_key.Append(":");
|
|
_key.Append(e.ResultType.Identity);
|
|
}
|
|
|
|
public override void Visit(DbFunctionExpression e)
|
|
{
|
|
VisitFunction(e.Function, e.Arguments);
|
|
}
|
|
|
|
public override void Visit(DbLambdaExpression expression)
|
|
{
|
|
_key.Append("Lambda(");
|
|
foreach (var v in expression.Lambda.Variables)
|
|
{
|
|
_key.Append("(V");
|
|
VisitVariableName(v.VariableName);
|
|
_key.Append(":");
|
|
_key.Append(v.ResultType.Identity);
|
|
_key.Append(')');
|
|
}
|
|
_key.Append("=");
|
|
foreach (var a in expression.Arguments)
|
|
{
|
|
_key.Append('(');
|
|
a.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
_key.Append(")Body(");
|
|
expression.Lambda.Body.Accept(this);
|
|
_key.Append(")");
|
|
}
|
|
|
|
public override void Visit(DbPropertyExpression e)
|
|
{
|
|
e.Instance.Accept(this);
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append(e.Property.Name);
|
|
}
|
|
|
|
public override void Visit(DbComparisonExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbLikeExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
e.Argument.Accept(this);
|
|
_key.Append(")(");
|
|
e.Pattern.Accept(this);
|
|
_key.Append(")(");
|
|
if (e.Escape != null)
|
|
{
|
|
e.Escape.Accept(this);
|
|
}
|
|
e.Argument.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbLimitExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
if (e.WithTies)
|
|
{
|
|
_key.Append("WithTies");
|
|
}
|
|
_key.Append('(');
|
|
e.Argument.Accept(this);
|
|
_key.Append(")(");
|
|
e.Limit.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbIsNullExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbArithmeticExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
foreach (var a in e.Arguments)
|
|
{
|
|
_key.Append('(');
|
|
a.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
}
|
|
|
|
public override void Visit(DbAndExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbOrExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbNotExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbDistinctExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbElementExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbIsEmptyExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbUnionAllExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbIntersectExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbExceptExpression e)
|
|
{
|
|
VisitBinary(e);
|
|
}
|
|
|
|
public override void Visit(DbTreatExpression e)
|
|
{
|
|
VisitCastOrTreat(e);
|
|
}
|
|
|
|
public override void Visit(DbCastExpression e)
|
|
{
|
|
VisitCastOrTreat(e);
|
|
}
|
|
|
|
public override void Visit(DbIsOfExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
e.Argument.Accept(this);
|
|
_key.Append(":");
|
|
_key.Append(e.OfType.EdmType.Identity);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbOfTypeExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
e.Argument.Accept(this);
|
|
_key.Append(":");
|
|
_key.Append(e.OfType.EdmType.Identity);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbCaseExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
for (int idx = 0; idx < e.When.Count; idx++)
|
|
{
|
|
_key.Append("WHEN:(");
|
|
e.When[idx].Accept(this);
|
|
_key.Append(")THEN:(");
|
|
e.Then[idx].Accept(this);
|
|
}
|
|
_key.Append("ELSE:(");
|
|
e.Else.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbNewInstanceExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append(':');
|
|
_key.Append(e.ResultType.EdmType.Identity);
|
|
_key.Append('(');
|
|
foreach (var a in e.Arguments)
|
|
{
|
|
_key.Append('(');
|
|
a.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
if (e.HasRelatedEntityReferences)
|
|
{
|
|
foreach (DbRelatedEntityRef relatedRef in e.RelatedEntityReferences)
|
|
{
|
|
_key.Append("RE(A(");
|
|
_key.Append(relatedRef.SourceEnd.DeclaringType.Identity);
|
|
_key.Append(")(");
|
|
_key.Append(relatedRef.SourceEnd.Name);
|
|
_key.Append("->");
|
|
_key.Append(relatedRef.TargetEnd.Name);
|
|
_key.Append(")(");
|
|
relatedRef.TargetEntityReference.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
}
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbRefExpression e)
|
|
{
|
|
//
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append("(ESET(");
|
|
_key.Append(e.EntitySet.EntityContainer.Name);
|
|
_key.Append('.');
|
|
_key.Append(e.EntitySet.Name);
|
|
_key.Append(")T(");
|
|
_key.Append(TypeHelpers.GetEdmType<RefType>(e.ResultType).ElementType.FullName);
|
|
_key.Append(")(");
|
|
e.Argument.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbRelationshipNavigationExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
e.NavigationSource.Accept(this);
|
|
_key.Append(")A(");
|
|
_key.Append(e.NavigateFrom.DeclaringType.Identity);
|
|
_key.Append(")(");
|
|
_key.Append(e.NavigateFrom.Name);
|
|
_key.Append("->");
|
|
_key.Append(e.NavigateTo.Name);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbDerefExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbRefKeyExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbEntityRefExpression e)
|
|
{
|
|
VisitUnary(e);
|
|
}
|
|
|
|
public override void Visit(DbScanExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
_key.Append(e.Target.EntityContainer.Name);
|
|
_key.Append('.');
|
|
_key.Append(e.Target.Name);
|
|
_key.Append(':');
|
|
_key.Append(e.ResultType.EdmType.Identity);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbFilterExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
_key.Append('(');
|
|
e.Predicate.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbProjectExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
_key.Append('(');
|
|
e.Projection.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbCrossJoinExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
foreach (var i in e.Inputs)
|
|
{
|
|
VisitBinding(i);
|
|
}
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbJoinExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Left);
|
|
VisitBinding(e.Right);
|
|
_key.Append('(');
|
|
e.JoinCondition.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbApplyExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
VisitBinding(e.Apply);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbGroupByExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitGroupBinding(e.Input);
|
|
foreach (var k in e.Keys)
|
|
{
|
|
_key.Append("K(");
|
|
k.Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
foreach (var a in e.Aggregates)
|
|
{
|
|
var ga = a as DbGroupAggregate;
|
|
if (ga != null)
|
|
{
|
|
_key.Append("GA(");
|
|
Debug.Assert(ga.Arguments.Count == 1, "Group aggregate must have one argument.");
|
|
ga.Arguments[0].Accept(this);
|
|
_key.Append(')');
|
|
}
|
|
else
|
|
{
|
|
_key.Append("A:");
|
|
var fa = (DbFunctionAggregate)a;
|
|
if (fa.Distinct)
|
|
{
|
|
_key.Append("D:");
|
|
}
|
|
VisitFunction(fa.Function, fa.Arguments);
|
|
}
|
|
}
|
|
_key.Append(')');
|
|
}
|
|
|
|
private void VisitSortOrder(IList<DbSortClause> sortOrder)
|
|
{
|
|
_key.Append("SO(");
|
|
foreach (var clause in sortOrder)
|
|
{
|
|
_key.Append(clause.Ascending ? "ASC(" : "DESC(");
|
|
clause.Expression.Accept(this);
|
|
_key.Append(')');
|
|
if (!String.IsNullOrEmpty(clause.Collation))
|
|
{
|
|
_key.Append(":(");
|
|
_key.Append(clause.Collation);
|
|
_key.Append(')');
|
|
}
|
|
}
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbSkipExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
VisitSortOrder(e.SortOrder);
|
|
_key.Append('(');
|
|
e.Count.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
|
|
public override void Visit(DbSortExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
VisitSortOrder(e.SortOrder);
|
|
_key.Append(')');
|
|
}
|
|
|
|
public override void Visit(DbQuantifierExpression e)
|
|
{
|
|
VisitExprKind(e.ExpressionKind);
|
|
_key.Append('(');
|
|
VisitBinding(e.Input);
|
|
_key.Append('(');
|
|
e.Predicate.Accept(this);
|
|
_key.Append("))");
|
|
}
|
|
#endregion
|
|
}
|
|
}
|