Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DLinq / Dlinq / SqlClient / Reader / ObjectReaderCompiler.cs / 5 / ObjectReaderCompiler.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Data;
using System.Data.Common;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data.Linq.Provider;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
namespace System.Data.Linq.SqlClient {
using System.Data.Linq.SqlClient.Implementation;
using System.Diagnostics.CodeAnalysis;
#if ILGEN || DEBUG
namespace Implementation {
///
/// Internal interface type defining the operations dynamic materialization functions need to perform when
/// materializing objects, without reflecting/invoking privates.
/// This interface is required because our anonymously hosted materialization delegates
/// run under partial trust and cannot access non-public members of types in the fully trusted
/// framework assemblies.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Materializer", Justification = "Spelling is correct.")]
[SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Unknown reason.")]
public abstract class ObjectMaterializer where TDataReader : DbDataReader {
// These are public fields rather than properties for access speed
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public int[] Ordinals;
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Globals", Justification = "Spelling is correct.")]
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public object[] Globals;
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public object[] Locals;
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public object[] Arguments;
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public TDataReader DataReader;
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "[....]: This is a public type that is not intended for public use.")]
public DbDataReader BufferReader;
public ObjectMaterializer() {
DataReader = default(TDataReader);
}
public abstract object InsertLookup(int globalMetaType, object instance);
public abstract void SendEntityMaterialized(int globalMetaType, object instance);
public abstract IEnumerable ExecuteSubQuery(int iSubQuery, object[] args);
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "[....]: Generic parameters are required for strong-typing of the return type.")]
public abstract IEnumerable GetLinkSource(int globalLink, int localFactory, object[] keyValues);
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "[....]: Generic parameters are required for strong-typing of the return type.")]
public abstract IEnumerable GetNestedLinkSource(int globalLink, int localFactory, object instance);
public abstract bool Read();
public abstract bool CanDeferLoad { get; }
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "[....]: Generic parameters are required for strong-typing of the return type.")]
public static IEnumerable Convert(IEnumerable source) {
foreach (object value in source) {
yield return DBConvert.ChangeType(value);
}
}
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
public static IGrouping CreateGroup(TKey key, IEnumerable items) {
return new ObjectReaderCompiler.Group(key, items);
}
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
public static IOrderedEnumerable CreateOrderedEnumerable(IEnumerable items) {
return new ObjectReaderCompiler.OrderedResults(items);
}
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
public static Exception ErrorAssignmentToNull(Type type) {
return Error.CannotAssignNull(type);
}
}
}
internal class ObjectReaderCompiler : IObjectReaderCompiler {
Type dataReaderType;
IDataServices services;
MethodInfo miDRisDBNull;
MethodInfo miBRisDBNull;
FieldInfo readerField;
FieldInfo bufferReaderField;
FieldInfo ordinalsField;
FieldInfo globalsField;
FieldInfo argsField;
#if DEBUG
static AssemblyBuilder captureAssembly;
static ModuleBuilder captureModule;
static string captureAssemblyFilename;
static int iCaptureId;
internal static int GetNextId() {
return iCaptureId++;
}
internal static ModuleBuilder CaptureModule {
get { return captureModule; }
}
internal static void StartCaptureToFile(string filename) {
if (captureAssembly == null) {
string dir = System.IO.Path.GetDirectoryName(filename);
if (dir.Length == 0) dir = null;
string name = System.IO.Path.GetFileName(filename);
AssemblyName assemblyName = new AssemblyName(System.IO.Path.GetFileNameWithoutExtension(name));
captureAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, dir);
captureModule = captureAssembly.DefineDynamicModule(name);
captureAssemblyFilename = filename;
}
}
internal static void StopCapture() {
if (captureAssembly != null) {
captureAssembly.Save(captureAssemblyFilename);
captureAssembly = null;
}
}
internal static void SetMaxReaderCacheSize(int size) {
if (size <= 1) {
throw Error.ArgumentOutOfRange("size");
}
maxReaderCacheSize = size;
}
#endif
static LocalDataStoreSlot cacheSlot = Thread.AllocateDataSlot();
static int maxReaderCacheSize = 10;
static ObjectReaderCompiler() {
}
internal ObjectReaderCompiler(Type dataReaderType, IDataServices services) {
this.dataReaderType = dataReaderType;
this.services = services;
this.miDRisDBNull = dataReaderType.GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
this.miBRisDBNull = typeof(DbDataReader).GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
this.ordinalsField = orbType.GetField("Ordinals", BindingFlags.Instance | BindingFlags.Public);
this.globalsField = orbType.GetField("Globals", BindingFlags.Instance | BindingFlags.Public);
this.argsField = orbType.GetField("Arguments", BindingFlags.Instance | BindingFlags.Public);
this.readerField = orbType.GetField("DataReader", BindingFlags.Instance | BindingFlags.Public);
this.bufferReaderField = orbType.GetField("BufferReader", BindingFlags.Instance | BindingFlags.Public);
System.Diagnostics.Debug.Assert(
this.miDRisDBNull != null &&
this.miBRisDBNull != null &&
this.readerField != null &&
this.bufferReaderField != null &&
this.ordinalsField != null &&
this.globalsField != null &&
this.argsField != null
);
}
public IObjectReaderFactory Compile(SqlExpression expression, Type elementType) {
object mapping = this.services.Context.Mapping.Identity;
DataLoadOptions options = this.services.Context.LoadOptions;
IObjectReaderFactory factory = null;
ReaderFactoryCache cache = null;
bool canBeCompared = SqlProjectionComparer.CanBeCompared(expression);
if (canBeCompared) {
cache = (ReaderFactoryCache)Thread.GetData(cacheSlot);
if (cache == null) {
cache = new ReaderFactoryCache(maxReaderCacheSize);
Thread.SetData(cacheSlot, cache);
}
factory = cache.GetFactory(elementType, this.dataReaderType, mapping, options, expression);
}
if (factory == null) {
Generator gen = new Generator(this, elementType);
#if DEBUG
if (ObjectReaderCompiler.CaptureModule != null) {
this.CompileCapturedMethod(gen, expression, elementType);
}
#endif
DynamicMethod dm = this.CompileDynamicMethod(gen, expression, elementType);
Type fnMatType = typeof(Func<,>).MakeGenericType(typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType), elementType);
var fnMaterialize = (Delegate)dm.CreateDelegate(fnMatType);
Type factoryType = typeof(ObjectReaderFactory<,>).MakeGenericType(this.dataReaderType, elementType);
factory = (IObjectReaderFactory)Activator.CreateInstance(
factoryType, BindingFlags.Instance | BindingFlags.NonPublic, null,
new object[] { fnMaterialize, gen.NamedColumns, gen.Globals, gen.Locals }, null
);
if (canBeCompared) {
expression = new SourceExpressionRemover().VisitExpression(expression);
cache.AddFactory(elementType, this.dataReaderType, mapping, options, expression, factory);
}
}
return factory;
}
private class SourceExpressionRemover : SqlDuplicator.DuplicatingVisitor {
internal SourceExpressionRemover()
: base(true) {
}
internal override SqlNode Visit(SqlNode node) {
node = base.Visit(node);
if (node != null) {
node.ClearSourceExpression();
}
return node;
}
internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
SqlExpression result = base.VisitColumnRef(cref);
if (result != null && result == cref) {
// reference to outer scope, don't propogate references to expressions or aliases
SqlColumn col = cref.Column;
SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
newcol.Ordinal = col.Ordinal;
result = new SqlColumnRef(newcol);
newcol.ClearSourceExpression();
}
return result;
}
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
SqlExpression result = base.VisitAliasRef(aref);
if (result != null && result == aref) {
// reference to outer scope, don't propogate references to expressions or aliases
SqlAlias alias = aref.Alias;
SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null));
return new SqlAliasRef(newalias);
}
return result;
}
}
public IObjectReaderSession CreateSession(DbDataReader reader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
Type sessionType = typeof(ObjectReaderSession<>).MakeGenericType(this.dataReaderType);
return (IObjectReaderSession)Activator.CreateInstance(sessionType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
new object[] { reader, provider, parentArgs, userArgs, subQueries }, null);
}
#if DEBUG
private void CompileCapturedMethod(Generator gen, SqlExpression expression, Type elementType) {
TypeBuilder tb = ObjectReaderCompiler.CaptureModule.DefineType("reader_type_" + ObjectReaderCompiler.GetNextId());
MethodBuilder mb = tb.DefineMethod(
"Read_" + elementType.Name,
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
elementType,
new Type[] { typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType) }
);
gen.GenerateBody(mb.GetILGenerator(), (SqlExpression)SqlDuplicator.Copy(expression));
tb.CreateType();
}
#endif
private DynamicMethod CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType) {
Type objectReaderType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
DynamicMethod dm = new DynamicMethod(
"Read_" + elementType.Name,
elementType,
new Type[] { objectReaderType },
true
);
gen.GenerateBody(dm.GetILGenerator(), expression);
return dm;
}
class ReaderFactoryCache {
int maxCacheSize;
LinkedList list;
class CacheInfo {
internal Type elementType;
internal Type dataReaderType;
internal object mapping;
internal DataLoadOptions options;
internal SqlExpression projection;
internal IObjectReaderFactory factory;
public CacheInfo(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
this.elementType = elementType;
this.dataReaderType = dataReaderType;
this.options = options;
this.mapping = mapping;
this.projection = projection;
this.factory = factory;
}
}
internal ReaderFactoryCache(int maxCacheSize) {
this.maxCacheSize = maxCacheSize;
this.list = new LinkedList();
}
internal IObjectReaderFactory GetFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection) {
for (LinkedListNode info = this.list.First; info != null; info = info.Next) {
if (elementType == info.Value.elementType &&
dataReaderType == info.Value.dataReaderType &&
mapping == info.Value.mapping &&
ShapesAreSimilar(options, info.Value.options) &&
SqlProjectionComparer.AreSimilar(projection, info.Value.projection)
) {
// move matching item to head of list to reset its lifetime
this.list.Remove(info);
this.list.AddFirst(info);
return info.Value.factory;
}
}
return null;
}
private static bool ShapesAreSimilar(DataLoadOptions ds1, DataLoadOptions ds2) {
return (ds1 == ds2) || ((ds1 == null || ds1.IsEmpty) && (ds2 == null || ds2.IsEmpty));
}
internal void AddFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
this.list.AddFirst(new LinkedListNode(new CacheInfo(elementType, dataReaderType, mapping, options, projection, factory)));
if (this.list.Count > this.maxCacheSize) {
this.list.RemoveLast();
}
}
}
internal class SqlProjectionComparer {
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
internal static bool CanBeCompared(SqlExpression node) {
if (node == null) {
return true;
}
switch (node.NodeType) {
case SqlNodeType.New: {
SqlNew new1 = (SqlNew)node;
for (int i = 0, n = new1.Args.Count; i < n; i++) {
if (!CanBeCompared(new1.Args[i])) {
return false;
}
}
for (int i = 0, n = new1.Members.Count; i < n; i++) {
if (!CanBeCompared(new1.Members[i].Expression)) {
return false;
}
}
return true;
}
case SqlNodeType.ColumnRef:
case SqlNodeType.Value:
case SqlNodeType.UserColumn:
return true;
case SqlNodeType.Link: {
SqlLink l1 = (SqlLink)node;
for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
if (!CanBeCompared(l1.KeyExpressions[i])) {
return false;
}
}
return true;
}
case SqlNodeType.OptionalValue:
return CanBeCompared(((SqlOptionalValue)node).Value);
case SqlNodeType.ValueOf:
case SqlNodeType.OuterJoinedValue:
return CanBeCompared(((SqlUnary)node).Operand);
case SqlNodeType.Lift:
return CanBeCompared(((SqlLift)node).Expression);
case SqlNodeType.Grouping: {
SqlGrouping g1 = (SqlGrouping)node;
return CanBeCompared(g1.Key) && CanBeCompared(g1.Group);
}
case SqlNodeType.ClientArray: {
SqlClientArray a1 = (SqlClientArray)node;
for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
if (!CanBeCompared(a1.Expressions[i])) {
return false;
}
}
return true;
}
case SqlNodeType.ClientCase: {
SqlClientCase c1 = (SqlClientCase)node;
for (int i = 0, n = c1.Whens.Count; i < n; i++) {
if (!CanBeCompared(c1.Whens[i].Match) ||
!CanBeCompared(c1.Whens[i].Value)) {
return false;
}
}
return true;
}
case SqlNodeType.SearchedCase: {
SqlSearchedCase c1 = (SqlSearchedCase)node;
for (int i = 0, n = c1.Whens.Count; i < n; i++) {
if (!CanBeCompared(c1.Whens[i].Match) ||
!CanBeCompared(c1.Whens[i].Value)) {
return false;
}
}
return CanBeCompared(c1.Else);
}
case SqlNodeType.TypeCase: {
SqlTypeCase c1 = (SqlTypeCase)node;
if (!CanBeCompared(c1.Discriminator)) {
return false;
}
for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
if (!CanBeCompared(c1.Whens[i].Match)) {
return false;
}
if (!CanBeCompared(c1.Whens[i].TypeBinding)) {
return false;
}
}
return true;
}
case SqlNodeType.DiscriminatedType:
return CanBeCompared(((SqlDiscriminatedType)node).Discriminator);
case SqlNodeType.JoinedCollection: {
SqlJoinedCollection j1 = (SqlJoinedCollection)node;
return CanBeCompared(j1.Count) && CanBeCompared(j1.Expression);
}
case SqlNodeType.Member:
return CanBeCompared(((SqlMember)node).Expression);
case SqlNodeType.MethodCall: {
SqlMethodCall mc = (SqlMethodCall)node;
if (mc.Object != null && !CanBeCompared(mc.Object)) {
return false;
}
for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
if (!CanBeCompared(mc.Arguments[0])) {
return false;
}
}
return true;
}
case SqlNodeType.ClientQuery:
return true;
case SqlNodeType.ClientParameter:
default:
return false;
}
}
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
internal static bool AreSimilar(SqlExpression node1, SqlExpression node2) {
if (node1 == node2) {
return true;
}
if (node1 == null || node2 == null) {
return false;
}
if (node1.NodeType != node2.NodeType ||
node1.ClrType != node2.ClrType ||
node1.SqlType != node2.SqlType) {
return false;
}
switch (node1.NodeType) {
case SqlNodeType.New: {
SqlNew new1 = (SqlNew)node1;
SqlNew new2 = (SqlNew)node2;
if (new1.Args.Count != new2.Args.Count ||
new1.Members.Count != new2.Members.Count) {
return false;
}
for (int i = 0, n = new1.Args.Count; i < n; i++) {
if (!AreSimilar(new1.Args[i], new2.Args[i])) {
return false;
}
}
for (int i = 0, n = new1.Members.Count; i < n; i++) {
if (!MetaPosition.AreSameMember(new1.Members[i].Member, new2.Members[i].Member) ||
!AreSimilar(new1.Members[i].Expression, new2.Members[i].Expression)) {
return false;
}
}
return true;
}
case SqlNodeType.ColumnRef: {
SqlColumnRef cref1 = (SqlColumnRef)node1;
SqlColumnRef cref2 = (SqlColumnRef)node2;
return cref1.Column.Ordinal == cref2.Column.Ordinal;
}
case SqlNodeType.Link: {
SqlLink l1 = (SqlLink)node1;
SqlLink l2 = (SqlLink)node2;
if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
return false;
}
if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
return false;
}
for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
if (!AreSimilar(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
return false;
}
}
return true;
}
case SqlNodeType.Value:
return Object.Equals(((SqlValue)node1).Value, ((SqlValue)node2).Value);
case SqlNodeType.OptionalValue: {
SqlOptionalValue ov1 = (SqlOptionalValue)node1;
SqlOptionalValue ov2 = (SqlOptionalValue)node2;
return AreSimilar(ov1.Value, ov2.Value);
}
case SqlNodeType.ValueOf:
case SqlNodeType.OuterJoinedValue:
return AreSimilar(((SqlUnary)node1).Operand, ((SqlUnary)node2).Operand);
case SqlNodeType.Lift:
return AreSimilar(((SqlLift)node1).Expression, ((SqlLift)node2).Expression);
case SqlNodeType.Grouping: {
SqlGrouping g1 = (SqlGrouping)node1;
SqlGrouping g2 = (SqlGrouping)node2;
return AreSimilar(g1.Key, g2.Key) && AreSimilar(g1.Group, g2.Group);
}
case SqlNodeType.ClientArray: {
SqlClientArray a1 = (SqlClientArray)node1;
SqlClientArray a2 = (SqlClientArray)node2;
if (a1.Expressions.Count != a2.Expressions.Count) {
return false;
}
for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
if (!AreSimilar(a1.Expressions[i], a2.Expressions[i])) {
return false;
}
}
return true;
}
case SqlNodeType.UserColumn:
return ((SqlUserColumn)node1).Name == ((SqlUserColumn)node2).Name;
case SqlNodeType.ClientCase: {
SqlClientCase c1 = (SqlClientCase)node1;
SqlClientCase c2 = (SqlClientCase)node2;
if (c1.Whens.Count != c2.Whens.Count) {
return false;
}
for (int i = 0, n = c1.Whens.Count; i < n; i++) {
if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
!AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value)) {
return false;
}
}
return true;
}
case SqlNodeType.SearchedCase: {
SqlSearchedCase c1 = (SqlSearchedCase)node1;
SqlSearchedCase c2 = (SqlSearchedCase)node2;
if (c1.Whens.Count != c2.Whens.Count) {
return false;
}
for (int i = 0, n = c1.Whens.Count; i < n; i++) {
if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
!AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value))
return false;
}
return AreSimilar(c1.Else, c2.Else);
}
case SqlNodeType.TypeCase: {
SqlTypeCase c1 = (SqlTypeCase)node1;
SqlTypeCase c2 = (SqlTypeCase)node2;
if (!AreSimilar(c1.Discriminator, c2.Discriminator)) {
return false;
}
if (c1.Whens.Count != c2.Whens.Count) {
return false;
}
for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match)) {
return false;
}
if (!AreSimilar(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
return false;
}
}
return true;
}
case SqlNodeType.DiscriminatedType: {
SqlDiscriminatedType dt1 = (SqlDiscriminatedType)node1;
SqlDiscriminatedType dt2 = (SqlDiscriminatedType)node2;
return AreSimilar(dt1.Discriminator, dt2.Discriminator);
}
case SqlNodeType.JoinedCollection: {
SqlJoinedCollection j1 = (SqlJoinedCollection)node1;
SqlJoinedCollection j2 = (SqlJoinedCollection)node2;
return AreSimilar(j1.Count, j2.Count) && AreSimilar(j1.Expression, j2.Expression);
}
case SqlNodeType.Member: {
SqlMember m1 = (SqlMember)node1;
SqlMember m2 = (SqlMember)node2;
return m1.Member == m2.Member && AreSimilar(m1.Expression, m2.Expression);
}
case SqlNodeType.ClientQuery: {
SqlClientQuery cq1 = (SqlClientQuery)node1;
SqlClientQuery cq2 = (SqlClientQuery)node2;
if (cq1.Arguments.Count != cq2.Arguments.Count) {
return false;
}
for (int i = 0, n = cq1.Arguments.Count; i < n; i++) {
if (!AreSimilar(cq1.Arguments[i], cq2.Arguments[i])) {
return false;
}
}
return true;
}
case SqlNodeType.MethodCall: {
SqlMethodCall mc1 = (SqlMethodCall)node1;
SqlMethodCall mc2 = (SqlMethodCall)node2;
if (mc1.Method != mc2.Method || !AreSimilar(mc1.Object, mc2.Object)) {
return false;
}
if (mc1.Arguments.Count != mc2.Arguments.Count) {
return false;
}
for (int i = 0, n = mc1.Arguments.Count; i < n; i++) {
if (!AreSimilar(mc1.Arguments[i], mc2.Arguments[i])) {
return false;
}
}
return true;
}
case SqlNodeType.ClientParameter:
default:
return false;
}
}
}
class SideEffectChecker : SqlVisitor {
bool hasSideEffect;
internal bool HasSideEffect(SqlNode node) {
this.hasSideEffect = false;
this.Visit(node);
return this.hasSideEffect;
}
internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
this.hasSideEffect = true;
return jc;
}
internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
return cq;
}
}
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unknown reason.")]
class Generator {
ObjectReaderCompiler compiler;
ILGenerator gen;
List