diff --git a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerCollection.cs b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerCollection.cs deleted file mode 100644 index 701575f20b..0000000000 --- a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerCollection.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Denis Krjuchkov -// Created: 2010.03.10 - -using System; -using System.Collections.Generic; -using System.Reflection; -using Xtensive.Reflection; - -namespace Xtensive.Orm.Linq.MemberCompilation -{ - internal sealed class MemberCompilerCollection - { - private readonly Dictionary items - = new Dictionary(); - - public int Count { get { return items.Count; } } - - public void Add(MemberCompilerRegistration registration) - { - items.Add(registration.TargetMember, registration); - } - - public MemberCompilerRegistration Get(MemberInfo member) - { - MemberCompilerRegistration result; - items.TryGetValue(member, out result); - return result; - } - - public void MergeWith(MemberCompilerCollection otherSource, bool failOnDuplicate) - { - if (failOnDuplicate) { - foreach (var key in otherSource.items.Keys) - if (items.ContainsKey(key)) - throw new InvalidOperationException(string.Format( - Strings.ExCompilerForXIsAlreadyRegistered, key.GetFullName(true))); - } - - foreach (var registration in otherSource.items.Values) - items[registration.TargetMember] = registration; - } - - - // Constructors - - public MemberCompilerCollection() - { - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs index f3e7fa7129..27bb889fe2 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs @@ -17,21 +17,42 @@ namespace Xtensive.Orm.Linq.MemberCompilation { internal partial class MemberCompilerProvider : LockableBase, IMemberCompilerProvider { - private MemberCompilerCollection compilers = new MemberCompilerCollection(); + private readonly struct CompilerKey : IEquatable + { + private readonly Module module; + private readonly int metadataToken; + + public bool Equals(CompilerKey other) => metadataToken == other.metadataToken + && (ReferenceEquals(module, other.module) || module == other.module); + + public override bool Equals(object obj) => obj is CompilerKey other && Equals(other); + + public override int GetHashCode() + { + unchecked { + return module == null ? metadataToken : (module.GetHashCode() * 397) ^ metadataToken; + } + } - public Type ExpressionType { get { return typeof(T); } } + public CompilerKey(MemberInfo memberInfo) + { + module = memberInfo.Module; + metadataToken = memberInfo.MetadataToken; + } + } + + private readonly Dictionary compilers + = new Dictionary(); + + public Type ExpressionType => typeof(T); public Delegate GetUntypedCompiler(MemberInfo target) { - ArgumentValidator.EnsureArgumentNotNull(target, "target"); - - var actualTarget = GetCanonicalMember(target); - if (actualTarget == null) - return null; - var registration = compilers.Get(actualTarget); - if (registration == null) - return null; - return registration.CompilerInvoker; + ArgumentValidator.EnsureArgumentNotNull(target, nameof(target)); + + return compilers.TryGetValue(GetCompilerKey(target), out var compiler) + ? compiler + : null; } public Func GetCompiler(MemberInfo target) @@ -54,16 +75,11 @@ public void RegisterCompilers(Type compilerContainer, ConflictHandlingMethod con throw new InvalidOperationException(string.Format( Strings.ExTypeXShouldNotBeGeneric, compilerContainer.GetFullName(true))); - var compilersToRegister = new MemberCompilerCollection(); - var compilerMethods = compilerContainer .GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) .Where(method => method.IsDefined(typeof (CompilerAttribute), false) && !method.IsGenericMethod); - foreach (var compiler in compilerMethods) - compilersToRegister.Add(ProcessCompiler(compiler)); - - UpdateRegistry(compilersToRegister, conflictHandlingMethod); + UpdateRegistry(compilerMethods.Select(ProcessCompiler), conflictHandlingMethod); } public void RegisterCompilers(IEnumerable>> compilerDefinitions) @@ -76,71 +92,38 @@ public void RegisterCompilers(IEnumerable (item.Key, (Delegate) item.Value)); UpdateRegistry(newItems, conflictHandlingMethod); } #region Private methods - private void UpdateRegistry(MemberCompilerCollection newItems, ConflictHandlingMethod conflictHandlingMethod) + private void UpdateRegistry( + IEnumerable<(MemberInfo targetMember, Delegate compiler)> newRegistrations, ConflictHandlingMethod conflictHandlingMethod) { - if (newItems.Count==0) - return; - switch (conflictHandlingMethod) { - case ConflictHandlingMethod.KeepOld: - newItems.MergeWith(compilers, false); - compilers = newItems; - break; - case ConflictHandlingMethod.Overwrite: - compilers.MergeWith(newItems, false); - break; - case ConflictHandlingMethod.ReportError: - compilers.MergeWith(newItems, true); - break; + foreach (var (targetMember, compiler) in newRegistrations) { + var key = GetCompilerKey(targetMember); + if (conflictHandlingMethod != ConflictHandlingMethod.Overwrite && compilers.ContainsKey(key)) { + if (conflictHandlingMethod == ConflictHandlingMethod.ReportError) { + throw new InvalidOperationException(string.Format( + Strings.ExCompilerForXIsAlreadyRegistered, targetMember.GetFullName(true))); + } + continue; + } + compilers[key] = compiler; } } - private static bool ParameterTypeMatches(Type inputParameterType, Type candidateParameterType) - { - return inputParameterType.IsGenericParameter - ? candidateParameterType==inputParameterType - : (candidateParameterType.IsGenericParameter || inputParameterType==candidateParameterType); - } - - private static bool AllParameterTypesMatch( - IEnumerable inputParameterTypes, IEnumerable candidateParameterTypes) - { - return inputParameterTypes - .Zip(candidateParameterTypes) - .All(pair => ParameterTypeMatches(pair.First, pair.Second)); - } - private static MethodBase GetCanonicalMethod(MethodBase inputMethod, MethodBase[] possibleCanonicalMethods) { - var inputParameterTypes = inputMethod.GetParameterTypes(); - - var candidates = possibleCanonicalMethods - .Where(candidate => candidate.Name==inputMethod.Name - && candidate.GetParameters().Length==inputParameterTypes.Length - && candidate.IsStatic==inputMethod.IsStatic) - .ToArray(); - - if (candidates.Length==0) - return null; - if (candidates.Length==1) - return candidates[0]; - - candidates = candidates - .Where(candidate => - AllParameterTypesMatch(inputParameterTypes, candidate.GetParameterTypes())) - .ToArray(); - - if (candidates.Length!=1) - return null; + foreach (var candidate in possibleCanonicalMethods) { + if (inputMethod.MetadataToken == candidate.MetadataToken + && (ReferenceEquals(inputMethod.Module, candidate.Module) || inputMethod.Module == candidate.Module)) { + return candidate; + } + } - return candidates[0]; + return null; } private static Type[] ValidateCompilerParametersAndExtractTargetSignature(MethodInfo compiler, bool requireMemberInfo) @@ -176,7 +159,7 @@ private static Type[] ValidateCompilerParametersAndExtractTargetSignature(Method return result; } - private static MemberCompilerRegistration ProcessCompiler(MethodInfo compiler) + private static (MemberInfo targetMember, Delegate compilerInvoker) ProcessCompiler(MethodInfo compiler) { var attribute = compiler.GetAttribute(AttributeSearchOptions.InheritNone); @@ -271,7 +254,7 @@ private static MemberCompilerRegistration ProcessCompiler(MethodInfo compiler) compiler.GetFullName(true))); var invoker = WrapInvoker(CreateInvoker(compiler, isStatic || isCtor, isGeneric)); - return new MemberCompilerRegistration(targetMember, invoker); + return (targetMember, invoker); } private static Func WrapInvoker(Func invoker) @@ -318,21 +301,18 @@ private static void ValidateCompilerParameter(ParameterInfo parameter, Type requ compiler.GetFullName(true), parameter.Name, requiredType.GetFullName(true))); } - private static MemberInfo GetCanonicalMember(MemberInfo member) + private static CompilerKey GetCompilerKey(MemberInfo member) { var canonicalMember = member; var sourceProperty = canonicalMember as PropertyInfo; if (sourceProperty!=null) { canonicalMember = sourceProperty.GetGetMethod(); // GetGetMethod returns null in case of non public getter. - if (canonicalMember==null) - return null; + if (canonicalMember==null) { + return default; + } } - var sourceMethod = canonicalMember as MethodInfo; - if (sourceMethod!=null && sourceMethod.IsGenericMethod) - canonicalMember = sourceMethod.GetGenericMethodDefinition(); - var targetType = canonicalMember.ReflectedType; if (targetType.IsGenericType) { targetType = targetType.GetGenericTypeDefinition(); @@ -348,7 +328,7 @@ private static MemberInfo GetCanonicalMember(MemberInfo member) } if (canonicalMember == null) { - return null; + return default; } if (targetType.IsEnum) { @@ -359,7 +339,7 @@ private static MemberInfo GetCanonicalMember(MemberInfo member) canonicalMember = GetCanonicalMethod((MethodInfo) canonicalMember, targetType.GetMethods()); } - return canonicalMember; + return new CompilerKey(canonicalMember); } #endregion diff --git a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerRegistration.cs b/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerRegistration.cs deleted file mode 100644 index adc618d95a..0000000000 --- a/Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerRegistration.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Denis Krjuchkov -// Created: 2011.10.16 - -using System; -using System.Reflection; -using Xtensive.Core; - -namespace Xtensive.Orm.Linq.MemberCompilation -{ - internal sealed class MemberCompilerRegistration - { - public readonly MemberInfo TargetMember; - public readonly Delegate CompilerInvoker; - - public MemberCompilerRegistration(MemberInfo targetMember, Delegate compilerInvoker) - { - ArgumentValidator.EnsureArgumentNotNull(targetMember, "targetMember"); - ArgumentValidator.EnsureArgumentNotNull(compilerInvoker, "compilerInvoker"); - - TargetMember = targetMember; - CompilerInvoker = compilerInvoker; - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Linq/Model/QueryParser.cs b/Orm/Xtensive.Orm/Orm/Linq/Model/QueryParser.cs index 495f2827c9..ac663a166f 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Model/QueryParser.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Model/QueryParser.cs @@ -7,6 +7,7 @@ using System; using System.Linq.Expressions; using Xtensive.Core; +using Xtensive.Reflection; namespace Xtensive.Orm.Linq.Model { @@ -14,29 +15,29 @@ internal static class QueryParser { public static GroupByQuery ParseGroupBy(MethodCallExpression mc) { - var method = mc.Method.GetGenericMethodDefinition(); + var method = mc.Method; - if (method==QueryableMethodInfo.GroupBy) + if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupBy)) return new GroupByQuery { Source = mc.Arguments[0], KeySelector = mc.Arguments[1].StripQuotes(), }; - if (method==QueryableMethodInfo.GroupByWithElementSelector) + if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithElementSelector)) return new GroupByQuery { Source = mc.Arguments[0], KeySelector = mc.Arguments[1].StripQuotes(), ElementSelector = mc.Arguments[2].StripQuotes(), }; - if (method==QueryableMethodInfo.GroupByWithResultSelector) + if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithResultSelector)) return new GroupByQuery { Source = mc.Arguments[0], KeySelector = mc.Arguments[1].StripQuotes(), ResultSelector = mc.Arguments[2].StripQuotes(), }; - if (method==QueryableMethodInfo.GroupByWithElementAndResultSelectors) + if (method.IsGenericMethodSpecificationOf(QueryableMethodInfo.GroupByWithElementAndResultSelectors)) return new GroupByQuery { Source = mc.Arguments[0], KeySelector = mc.Arguments[1].StripQuotes(), diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs index 984e4c444d..ce3e0ee0f5 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs @@ -327,103 +327,117 @@ protected override Expression VisitMethodCall(MethodCallExpression mc) { using (state.CreateScope()) { state.IsTailMethod = mc==context.Query && mc.IsQuery(); - var customCompiler = context.CustomCompilerProvider.GetCompiler(mc.Method); - if (customCompiler!=null) + var method = mc.Method; + var customCompiler = context.CustomCompilerProvider.GetCompiler(method); + if (customCompiler!=null) { return Visit(customCompiler.Invoke(mc.Object, mc.Arguments.ToArray())); + } // Visit Query. Deprecated. #pragma warning disable 612,618 - if (mc.Method.DeclaringType==typeof (Query)) { + if (method.DeclaringType==typeof (Query)) { // Query.All - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()==WellKnownMembers.Query.All) + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.All)) { return ConstructQueryable(mc); + } + // Query.FreeText - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() - .In(WellKnownMembers.Query.FreeTextString, - WellKnownMembers.Query.FreeTextExpression, - WellKnownMembers.Query.FreeTextExpressionTopNByRank, - WellKnownMembers.Query.FreeTextStringTopNByRank)) - return ConstructFreeTextQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments); + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextString) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextExpression) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextExpressionTopNByRank) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.FreeTextStringTopNByRank)) { + return ConstructFreeTextQueryRoot(method.GetGenericArguments()[0], mc.Arguments); + } + // Query.ContainsTable - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() - .In(WellKnownMembers.Query.ContainsTableExpr, - WellKnownMembers.Query.ContainsTableExprWithColumns, - WellKnownMembers.Query.ContainsTableExprTopNByRank, - WellKnownMembers.Query.ContainsTableExprWithColumnsTopNByRank)) - return ConstructContainsTableQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments); + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExpr) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprWithColumns) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprTopNByRank) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.ContainsTableExprWithColumnsTopNByRank)) { + return ConstructContainsTableQueryRoot(method.GetGenericArguments()[0], mc.Arguments); + } + // Query.Single & Query.SingleOrDefault - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition().In( - WellKnownMembers.Query.SingleKey, - WellKnownMembers.Query.SingleOrDefaultKey)) + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.SingleKey) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Query.SingleOrDefaultKey)) { return VisitQuerySingle(mc); + } + throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true))); } // Visit QueryEndpoint. - if (mc.Method.DeclaringType == typeof(QueryEndpoint)) { + if (method.DeclaringType == typeof(QueryEndpoint)) { // Query.All - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() == WellKnownMembers.QueryEndpoint.All) + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.All)) { return ConstructQueryable(mc); + } + // Query.FreeText - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() - .In(WellKnownMembers.QueryEndpoint.FreeTextString, - WellKnownMembers.QueryEndpoint.FreeTextExpression, - WellKnownMembers.Query.FreeTextExpressionTopNByRank, - WellKnownMembers.Query.FreeTextStringTopNByRank)) - return ConstructFreeTextQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments); + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextString) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextExpression) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextExpressionTopNByRank) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.FreeTextStringTopNByRank)) { + return ConstructFreeTextQueryRoot(method.GetGenericArguments()[0], mc.Arguments); + } + // Query.ContainsTable - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition() - .In(WellKnownMembers.QueryEndpoint.ContainsTableExpr, - WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumns, - WellKnownMembers.QueryEndpoint.ContainsTableExprTopNByRank, - WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumnsTopNByRank)) - return ConstructContainsTableQueryRoot(mc.Method.GetGenericArguments()[0], mc.Arguments); + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExpr) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumns) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprTopNByRank) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.ContainsTableExprWithColumnsTopNByRank)) { + return ConstructContainsTableQueryRoot(method.GetGenericArguments()[0], mc.Arguments); + } + // Query.Single & Query.SingleOrDefault - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition().In( - WellKnownMembers.QueryEndpoint.SingleKey, - WellKnownMembers.QueryEndpoint.SingleOrDefaultKey)) + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.SingleKey) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.SingleOrDefaultKey)) { return VisitQuerySingle(mc); - if (mc.Method.IsGenericMethod && mc.Method.GetGenericMethodDefinition()==WellKnownMembers.QueryEndpoint.Items) + } + + if (method.IsGenericMethodSpecificationOf(WellKnownMembers.QueryEndpoint.Items)) { return VisitSequence(mc.Arguments[0].StripQuotes().Body, mc); + } + throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true))); } #pragma warning restore 612,618 // Visit Queryable extensions. - if (mc.Method.DeclaringType==typeof (QueryableExtensions)) - if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionLeftJoin.Name) + if (method.DeclaringType==typeof (QueryableExtensions)) + if (method.Name==WellKnownMembers.Queryable.ExtensionLeftJoin.Name) return VisitLeftJoin(mc); - else if (mc.Method.Name=="In") + else if (method.Name=="In") return VisitIn(mc); - else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionLock.Name) + else if (method.Name==WellKnownMembers.Queryable.ExtensionLock.Name) return VisitLock(mc); - else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionTake.Name) + else if (method.Name==WellKnownMembers.Queryable.ExtensionTake.Name) return VisitTake(mc.Arguments[0], mc.Arguments[1]); - else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionSkip.Name) + else if (method.Name==WellKnownMembers.Queryable.ExtensionSkip.Name) return VisitSkip(mc.Arguments[0], mc.Arguments[1]); - else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionElementAt.Name) - return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.ReturnType, false); - else if (mc.Method.Name==WellKnownMembers.Queryable.ExtensionElementAtOrDefault.Name) - return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.ReturnType, true); - else if (mc.Method.Name == WellKnownMembers.Queryable.ExtensionCount.Name) - return VisitAggregate(mc.Arguments[0], mc.Method, null, context.IsRoot(mc), mc); + else if (method.Name==WellKnownMembers.Queryable.ExtensionElementAt.Name) + return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.ReturnType, false); + else if (method.Name==WellKnownMembers.Queryable.ExtensionElementAtOrDefault.Name) + return VisitElementAt(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.ReturnType, true); + else if (method.Name == WellKnownMembers.Queryable.ExtensionCount.Name) + return VisitAggregate(mc.Arguments[0], method, null, context.IsRoot(mc), mc); else throw new InvalidOperationException(String.Format(Strings.ExMethodCallExpressionXIsNotSupported, mc.ToString(true))); // Visit Collection extensions - if (mc.Method.DeclaringType==typeof (CollectionExtensions)) - if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsAny.Name) - return VisitContainsAny(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]); - else if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsAll.Name) - return VisitContainsAll(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]); - else if (mc.Method.Name==WellKnownMembers.Collection.ExtensionContainsNone.Name) - return VisitContainsNone(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), mc.Method.GetGenericArguments()[0]); + if (method.DeclaringType==typeof (CollectionExtensions)) + if (method.Name==WellKnownMembers.Collection.ExtensionContainsAny.Name) + return VisitContainsAny(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]); + else if (method.Name==WellKnownMembers.Collection.ExtensionContainsAll.Name) + return VisitContainsAll(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]); + else if (method.Name==WellKnownMembers.Collection.ExtensionContainsNone.Name) + return VisitContainsNone(mc.Arguments[0], mc.Arguments[1], context.IsRoot(mc), method.GetGenericArguments()[0]); // Process local collections if (mc.Object.IsLocalCollection(context)) { // IList.Contains - // List.Contains + // List.Contains // Array.Contains - ParameterInfo[] parameters = mc.Method.GetParameters(); - if (mc.Method.Name=="Contains" && parameters.Length==1) + ParameterInfo[] parameters = method.GetParameters(); + if (method.Name=="Contains" && parameters.Length==1) return VisitContains(mc.Object, mc.Arguments[0], false); } diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs index c6dc919ea3..52db2b8307 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs @@ -1086,12 +1086,12 @@ private ProjectionExpression VisitSelectMany(Expression source, LambdaExpression bool isOuter = false; if (collectionSelector.Body.NodeType==ExpressionType.Call) { var call = (MethodCallExpression) collectionSelector.Body; - var genericMethodDefinition = call.Method.GetGenericMethodDefinition(); - isOuter = call.Method.IsGenericMethod - && (genericMethodDefinition==WellKnownMembers.Queryable.DefaultIfEmpty - || genericMethodDefinition==WellKnownMembers.Enumerable.DefaultIfEmpty); - if (isOuter) + var method = call.Method; + isOuter = method.IsGenericMethodSpecificationOf(WellKnownMembers.Queryable.DefaultIfEmpty) + || method.IsGenericMethodSpecificationOf(WellKnownMembers.Enumerable.DefaultIfEmpty); + if (isOuter) { collectionSelector = FastExpression.Lambda(call.Arguments[0], outerParameter); + } } ProjectionExpression innerProjection; using (state.CreateScope()) { diff --git a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index d3da8d7181..c2f5a07671 100644 --- a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs +++ b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs @@ -891,6 +891,22 @@ public static bool IsNullable(this Type type) => /// object describing the delegate "Invoke" method. public static MethodInfo GetInvokeMethod(this Type delegateType) => delegateType.GetMethod(invokeMethodName); + + /// + /// Determines whether given is a specification + /// of the provided . + /// + /// The to check. + /// The of the generic method definition + /// to check against. + /// if the specified is a specification + /// of the provided . + public static bool IsGenericMethodSpecificationOf(this MethodInfo method, MethodInfo genericMethodDefinition) => + method.MetadataToken == genericMethodDefinition.MetadataToken + && (ReferenceEquals(method.Module, genericMethodDefinition.Module) + || method.Module == genericMethodDefinition.Module) + && method.IsGenericMethod && genericMethodDefinition.IsGenericMethodDefinition; + /// /// Determines whether the specified is an ancestor or an instance of the /// provided .