diff --git a/Orm/Xtensive.Orm.Tests.Core/Reflection/TypeHelperTest.cs b/Orm/Xtensive.Orm.Tests.Core/Reflection/TypeHelperTest.cs index a348841b87..9ff64c2574 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Reflection/TypeHelperTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Reflection/TypeHelperTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2007-2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Yakunin // Created: 2007.12.17 @@ -14,6 +14,7 @@ using Xtensive.Core; using Xtensive.Reflection; using Xtensive.Orm.Tests; +using System.Linq; namespace Xtensive.Orm.Tests.Core.Reflection { @@ -480,5 +481,47 @@ public void GenericIsNullableTest() Assert.IsFalse(TypeHelper.IsNullable()); Assert.IsFalse(TypeHelper.IsNullable()); } + + [Test] + public void IsValueTupleTest() + { + var tupleTypes = new[] { + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple), + typeof (ValueTuple) + }; + + var otherTypes = new[] { + typeof (string), + typeof (char), + typeof (bool), + typeof (DateTime), + typeof (TimeSpan), + typeof (Guid), + typeof (TypeCode), + typeof (byte[]), + typeof (Key), + this.GetType() + }; + + var startingToken = tupleTypes[0].MetadataToken; + + Assert.That( + tupleTypes.Select(t => t.MetadataToken - startingToken).SequenceEqual(Enumerable.Range(0, tupleTypes.Length)), + Is.True); + + foreach (var type in tupleTypes) { + Assert.IsTrue(type.IsValueTuple()); + } + + foreach (var type in otherTypes) { + Assert.IsFalse(type.IsValueTuple()); + } + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs index 47a71904d9..56ae7c26a1 100755 --- a/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs +++ b/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs @@ -19,7 +19,7 @@ namespace Xtensive.Orm.Tests.Issues namespace IssueGithub0224_DelayedQueryCapture_Model { [HierarchyRoot] - class Item : Entity + public class Item : Entity { [Field, Key] public int Id { get; private set; } @@ -32,50 +32,133 @@ class Item : Entity [Serializable] public class IssueGithub0224_DelayedQueryCapture : AutoBuildTest { - public class OtherService + + #region Services + + public class OtherService1 + { + public static volatile int InstanceCount; + + public int N; + + public OtherService1() + { + _ = Interlocked.Increment(ref InstanceCount); + } + + ~OtherService1() + { + _ = Interlocked.Decrement(ref InstanceCount); + } + } + + public class OtherService2 + { + public static volatile int InstanceCount; + + public int N; + + public OtherService2() + { + _ = Interlocked.Increment(ref InstanceCount); + } + + ~OtherService2() + { + _ = Interlocked.Decrement(ref InstanceCount); + } + } + + public class OtherService3 { public static volatile int InstanceCount; public int N; - public OtherService() + public OtherService3() { - Interlocked.Increment(ref InstanceCount); + _ = Interlocked.Increment(ref InstanceCount); } - ~OtherService() + ~OtherService3() { - Interlocked.Decrement(ref InstanceCount); + _ = Interlocked.Decrement(ref InstanceCount); } } + #endregion protected override DomainConfiguration BuildConfiguration() { var config = base.BuildConfiguration(); - config.Types.Register(typeof(Item).Assembly, typeof(Item).Namespace); + config.Types.Register(typeof(Item)); return config; } [Test] - public void DelayedQueryCapture() + public void DelayedQueryWithIncludeTest() + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var item = new Item() { Tag = 10 }; + DelayedQueryWithInclude(session); + t.Complete(); + } + TestHelper.CollectGarbage(true); + Assert.AreEqual(0, OtherService1.InstanceCount); + } + + [Test] + public void DelayedQueryWithContainsTest() + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var item = new Item() { Tag = 10 }; + DelayedQueryWithContains(session); + t.Complete(); + } + + TestHelper.CollectGarbage(true); + Assert.AreEqual(0, OtherService2.InstanceCount); + } + + [Test] + public void DelayedQueryWithEqualityTest() { using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var item = new Item() { Tag = 10 }; - DelayedQuery(session); + DelayedQueryWithEquality(session); t.Complete(); } - GC.Collect(); - Thread.Sleep(1000); - GC.Collect(); - Assert.AreEqual(0, OtherService.InstanceCount); + + TestHelper.CollectGarbage(true); + Assert.AreEqual(0, OtherService3.InstanceCount); + } + + private void DelayedQueryWithEquality(Session session) + { + var id = 1; + var otherService = new OtherService3(); + + var items = session.Query.CreateDelayedQuery("ABC", q => + from t in q.All() + where t.Id == id + select t).ToArray(); + + var bb1 = items + .Select(a => new { + a.Id, + A = new { + B = otherService.N == a.Id + }, + }); } - private void DelayedQuery(Session session) + private void DelayedQueryWithInclude(Session session) { var ids = new[] { 1, 2 }; - var otherService = new OtherService(); + var otherService = new OtherService1(); var items = session.Query.CreateDelayedQuery(q => from t in q.All() @@ -90,5 +173,24 @@ where t.Id.In(ids) }, }); } + + private void DelayedQueryWithContains(Session session) + { + var ids = new[] { 1, 2 }; + var otherService = new OtherService2(); + + var items = session.Query.CreateDelayedQuery(q => + from t in q.All() + where ids.Contains(t.Id) + select t).ToArray(); + + var bb1 = items + .Select(a => new { + a.Id, + A = new { + B = otherService.N == a.Id + }, + }); + } } } diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs index fa0dde9aeb..684a473415 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// Copyright (C) 2009-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexis Kochetov diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index e499166a64..cb4c2497c8 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -5,6 +5,7 @@ // Created: 2012.01.27 using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -20,8 +21,6 @@ namespace Xtensive.Orm.Internals { internal class CompiledQueryRunner { - private static readonly Func FieldIsSimple = fieldInfo => IsSimpleType(fieldInfo.FieldType); - private readonly Domain domain; private readonly Session session; private readonly QueryEndpoint endpoint; @@ -111,24 +110,24 @@ private DelayedQuery CreateDelayedSequenceQuery( private ParameterizedQuery GetScalarQuery( Func query, bool executeAsSideEffect, out TResult result) { - var cacheable = AllocateParameterAndReplacer(); + AllocateParameterAndReplacer(); var parameterContext = new ParameterContext(outerContext); parameterContext.SetValue(queryParameter, queryTarget); var scope = new CompiledQueryProcessingScope( queryParameter, queryParameterReplacer, parameterContext, executeAsSideEffect); + using (scope.Enter()) { result = query.Invoke(endpoint); } var parameterizedQuery = (ParameterizedQuery) scope.ParameterizedQuery; - if (parameterizedQuery==null && queryTarget!=null) { + if (parameterizedQuery == null && queryTarget != null) { throw new NotSupportedException(Strings.ExNonLinqCallsAreNotSupportedWithinQueryExecuteDelayed); } - if (cacheable) { - PutCachedQuery(parameterizedQuery); - } + PutQueryToCache(parameterizedQuery); + return parameterizedQuery; } @@ -140,7 +139,7 @@ private ParameterizedQuery GetSequenceQuery( return parameterizedQuery; } - var cacheable = AllocateParameterAndReplacer(); + AllocateParameterAndReplacer(); var scope = new CompiledQueryProcessingScope(queryParameter, queryParameterReplacer); using (scope.Enter()) { var result = query.Invoke(endpoint); @@ -148,18 +147,17 @@ private ParameterizedQuery GetSequenceQuery( parameterizedQuery = (ParameterizedQuery) translatedQuery; } - if (cacheable) { - PutCachedQuery(parameterizedQuery); - } + PutQueryToCache(parameterizedQuery); + return parameterizedQuery; } - private bool AllocateParameterAndReplacer() + private void AllocateParameterAndReplacer() { if (queryTarget == null) { queryParameter = null; queryParameterReplacer = new ExtendedExpressionReplacer(e => e); - return true; + return; } var closureType = queryTarget.GetType(); @@ -199,31 +197,12 @@ private bool AllocateParameterAndReplacer() } return null; }); - - return !TypeHelper.IsClosure(closureType) - || closureType.GetFields().All(FieldIsSimple); - } - - private static bool IsSimpleType(Type type) - { - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsGenericType) { - var genericDef = typeInfo.GetGenericTypeDefinition(); - return (genericDef == WellKnownTypes.NullableOfT || genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT)) - && IsSimpleType(typeInfo.GetGenericArguments()[0]); - } - else if (typeInfo.IsArray) { - return IsSimpleType(typeInfo.GetElementType()); - } - else { - return typeInfo.IsPrimitive || typeInfo.IsEnum || type == WellKnownTypes.String || type == WellKnownTypes.Decimal; - } } private ParameterizedQuery GetCachedQuery() => domain.QueryCache.TryGetItem(queryKey, true, out var item) ? item.Second : null; - private void PutCachedQuery(ParameterizedQuery parameterizedQuery) => + private void PutQueryToCache(ParameterizedQuery parameterizedQuery) => domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); private ParameterContext CreateParameterContext(ParameterizedQuery query) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/LocalCollectionExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/LocalCollectionExpression.cs index ffcd4e9676..2e2eef5584 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/LocalCollectionExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/LocalCollectionExpression.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexey Gamzov // Created: 2009.09.09 @@ -18,32 +18,28 @@ namespace Xtensive.Orm.Linq.Expressions internal class LocalCollectionExpression : ParameterizedExpression, IMappedExpression { - public Expression SourceExpression { get;private set; } + // just to have good error + private readonly string expressionAsString; + public IDictionary Fields { get; set; } - public Expression MaterializationExpression { get; private set; } + public MemberInfo MemberInfo { get; private set; } - public IEnumerable Columns - { - get - { - return Fields - .SelectMany(field => field.Value is ColumnExpression - ? EnumerableUtils.One(field.Value) - : ((LocalCollectionExpression) field.Value).Columns.Cast()) - .Cast(); - } - } + public IEnumerable Columns => + Fields + .SelectMany(field => field.Value is ColumnExpression + ? EnumerableUtils.One(field.Value) + : ((LocalCollectionExpression) field.Value).Columns.Cast()) + .Cast(); public Expression Remap(int offset, Dictionary processedExpressions) { if (!CanRemap) return this; - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) return value; - var result = new LocalCollectionExpression(Type, MemberInfo, SourceExpression); + var result = new LocalCollectionExpression(Type, MemberInfo, expressionAsString); processedExpressions.Add(this, result); result.Fields = Fields.ToDictionary(f=>f.Key, f=>(IMappedExpression)f.Value.Remap(offset, processedExpressions)); return result; @@ -53,11 +49,10 @@ public Expression Remap(IReadOnlyList map, Dictionaryf.Key, f=>(IMappedExpression)f.Value.Remap(map, processedExpressions)); return result; @@ -65,11 +60,10 @@ public Expression Remap(IReadOnlyList map, Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) return value; - var result = new LocalCollectionExpression(Type, MemberInfo, SourceExpression); + var result = new LocalCollectionExpression(Type, MemberInfo, expressionAsString); processedExpressions.Add(this, result); result.Fields = Fields.ToDictionary(f=>f.Key, f=>(IMappedExpression)f.Value.BindParameter(parameter, processedExpressions)); return result; @@ -77,23 +71,32 @@ public Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) return value; - var result = new LocalCollectionExpression(Type, MemberInfo, SourceExpression); + var result = new LocalCollectionExpression(Type, MemberInfo, expressionAsString); processedExpressions.Add(this, result); result.Fields = Fields.ToDictionary(f=>f.Key, f=>(IMappedExpression)f.Value.RemoveOuterParameter(processedExpressions)); return result; } + public override string ToString() => expressionAsString; + public LocalCollectionExpression(Type type, MemberInfo memberInfo, Expression sourceExpression) : base(ExtendedExpressionType.LocalCollection, type, null, true) { Fields = new Dictionary(); MemberInfo = memberInfo; - SourceExpression = sourceExpression; + expressionAsString = sourceExpression.ToString(); + ; } + private LocalCollectionExpression(Type type, MemberInfo memberInfo, in string stringRepresentation) + : base(ExtendedExpressionType.LocalCollection, type, null, true) + { + Fields = new Dictionary(); + MemberInfo = memberInfo; + expressionAsString = stringRepresentation; + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs index cbed9e56ea..ddb897a6c1 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2021 Xtensive LLC. +// Copyright (C) 2009-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Gamzov @@ -57,11 +57,12 @@ public void AddRange(IReadOnlyCollection newTypes) private readonly Func> enumerableFunc; private readonly DomainModel model; - private Func converter; - private readonly Expression sourceExpression; private readonly Type entityTypestoredInKey; private readonly bool isKeyConverter; + private Func converter; + + public override Expression>> GetEnumerable() { var call = Expression.Call(Expression.Constant(enumerableFunc.Target), enumerableFunc.Method, ParamContext); @@ -76,16 +77,15 @@ private bool IsPersistableType(Type type) if (type==WellKnownOrmTypes.Entity || type.IsSubclassOf(WellKnownOrmTypes.Entity) || type==WellKnownOrmTypes.Structure - || type.IsSubclassOf(WellKnownOrmTypes.Structure) - ) { + || type.IsSubclassOf(WellKnownOrmTypes.Structure)) { if (!model.Types.Contains(type)) - throw new InvalidOperationException(String.Format(Strings.ExTypeNotFoundInModel, type.FullName)); + throw new InvalidOperationException(string.Format(Strings.ExTypeNotFoundInModel, type.FullName)); return true; } if (type.IsOfGenericType(RefOfTType)) { var entityType = type.GetGenericType(RefOfTType).GetGenericArguments()[0]; if (!model.Types.Contains(entityType)) - throw new InvalidOperationException(String.Format(Strings.ExTypeNotFoundInModel, type.FullName)); + throw new InvalidOperationException(string.Format(Strings.ExTypeNotFoundInModel, type.FullName)); return true; } return TypeIsStorageMappable(type); @@ -107,7 +107,7 @@ private static bool TypeIsStorageMappable(Type type) } - private void FillLocalCollectionField(object item, Tuple tuple, Expression expression) + private static void FillLocalCollectionField(object item, Tuple tuple, Expression expression) { if (item==null) return; @@ -115,11 +115,14 @@ private void FillLocalCollectionField(object item, Tuple tuple, Expression expre switch (expression) { case LocalCollectionExpression itemExpression: foreach (var field in itemExpression.Fields) { - var propertyInfo = field.Key as PropertyInfo; - object value = propertyInfo==null - ? ((FieldInfo) field.Key).GetValue(item) - : propertyInfo.GetValue(item, BindingFlags.InvokeMethod, null, null, null); - if (value!=null) + object value; + if (field.Key is PropertyInfo propertyInfo) { + value = propertyInfo.GetValue(item, BindingFlags.InvokeMethod, null, null, null); + } + else { + value = ((FieldInfo) field.Key).GetValue(item); + } + if (value != null) FillLocalCollectionField(value, tuple, (Expression) field.Value); } break; @@ -151,39 +154,39 @@ private void FillLocalCollectionField(object item, Tuple tuple, Expression expre } } - private LocalCollectionExpression BuildLocalCollectionExpression(Type type, HashSet processedTypes, ref int columnIndex, MemberInfo parentMember, TupleTypeCollection types) + private LocalCollectionExpression BuildLocalCollectionExpression(Type type, + HashSet processedTypes, ref int columnIndex, MemberInfo parentMember, TupleTypeCollection types, Expression sourceExpression) { if (type.IsAssignableFrom(WellKnownOrmTypes.Key)) - throw new InvalidOperationException(String.Format(Strings.ExUnableToStoreUntypedKeyToStorage, RefOfTType.GetShortName())); + throw new InvalidOperationException(string.Format(Strings.ExUnableToStoreUntypedKeyToStorage, RefOfTType.GetShortName())); if (!processedTypes.Add(type)) - throw new InvalidOperationException(String.Format(Strings.ExUnableToPersistTypeXBecauseOfLoopReference, type.FullName)); + throw new InvalidOperationException(string.Format(Strings.ExUnableToPersistTypeXBecauseOfLoopReference, type.FullName)); - IEnumerable members = type + var members = type .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(propertyInfo => propertyInfo.CanRead) .Cast() .Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public)); var fields = new Dictionary(); - foreach (MemberInfo memberInfo in members) { + foreach (var memberInfo in members) { var propertyInfo = memberInfo as PropertyInfo; - Type memberType = propertyInfo==null + var memberType = propertyInfo==null ? ((FieldInfo) memberInfo).FieldType : propertyInfo.PropertyType; if (IsPersistableType(memberType)) { - IMappedExpression expression = BuildField(memberType, ref columnIndex, types); + var expression = BuildField(memberType, ref columnIndex, types); fields.Add(memberInfo, expression); } else { - LocalCollectionExpression collectionExpression = BuildLocalCollectionExpression(memberType, new HashSet(processedTypes), ref columnIndex, memberInfo, types); + var collectionExpression = BuildLocalCollectionExpression(memberType, new HashSet(processedTypes), ref columnIndex, memberInfo, types, sourceExpression); fields.Add(memberInfo, collectionExpression); } } if (fields.Count==0) - throw new InvalidOperationException(String.Format(Strings.ExTypeXDoesNotHasAnyPublicReadablePropertiesOrFieldsSoItCanTBePersistedToStorage, type.FullName)); - var result = new LocalCollectionExpression(type, parentMember, sourceExpression); - result.Fields = fields; - return result; + throw new InvalidOperationException(string.Format(Strings.ExTypeXDoesNotHasAnyPublicReadablePropertiesOrFieldsSoItCanTBePersistedToStorage, type.FullName)); + + return new LocalCollectionExpression(type, parentMember, sourceExpression) { Fields = fields }; } @@ -201,9 +204,9 @@ private IMappedExpression BuildField(Type type, ref int index, TupleTypeCollecti // } if (type.IsSubclassOf(WellKnownOrmTypes.Entity)) { - TypeInfo typeInfo = model.Types[type]; - KeyInfo keyInfo = typeInfo.Key; - TupleDescriptor keyTupleDescriptor = keyInfo.TupleDescriptor; + var typeInfo = model.Types[type]; + var keyInfo = typeInfo.Key; + var keyTupleDescriptor = keyInfo.TupleDescriptor; IMappedExpression expression; if (isKeyConverter) expression = KeyExpression.Create(typeInfo, index); @@ -218,10 +221,10 @@ private IMappedExpression BuildField(Type type, ref int index, TupleTypeCollecti } if (type.IsSubclassOf(WellKnownOrmTypes.Structure)) { - TypeInfo typeInfo = model.Types[type]; - TupleDescriptor tupleDescriptor = typeInfo.TupleDescriptor; + var typeInfo = model.Types[type]; + var tupleDescriptor = typeInfo.TupleDescriptor; var tupleSegment = new Segment(index, tupleDescriptor.Count); - StructureExpression structureExpression = StructureExpression.CreateLocalCollectionStructure(typeInfo, tupleSegment); + var structureExpression = StructureExpression.CreateLocalCollectionStructure(typeInfo, tupleSegment); index += tupleDescriptor.Count; types.AddRange(tupleDescriptor); return structureExpression; @@ -237,7 +240,7 @@ private IMappedExpression BuildField(Type type, ref int index, TupleTypeCollecti throw new NotSupportedException(); } - private void BuildConverter() + private void BuildConverter(Expression sourceExpression) { var itemType = isKeyConverter ? entityTypestoredInKey : typeof (TItem); var index = 0; @@ -248,13 +251,13 @@ private void BuildConverter() } else { var processedTypes = new HashSet(); - var itemExpression = BuildLocalCollectionExpression(itemType, processedTypes, ref index, null, types); + var itemExpression = BuildLocalCollectionExpression(itemType, processedTypes, ref index, null, types, sourceExpression); TupleDescriptor = TupleDescriptor.Create(types.ToArray(types.Count)); Expression = itemExpression; } converter = delegate(TItem item) { - Tuple tuple = Tuple.Create(TupleDescriptor); + var tuple = Tuple.Create(TupleDescriptor); if (ReferenceEquals(item, null)) { return tuple; } @@ -267,10 +270,9 @@ public ItemToTupleConverter(Func> enumerabl { this.model = model; this.enumerableFunc = enumerableFunc; - this.sourceExpression = sourceExpression; - this.entityTypestoredInKey = storedEntityType; - isKeyConverter = typeof (TItem).IsAssignableFrom(WellKnownOrmTypes.Key); - BuildConverter(); + entityTypestoredInKey = storedEntityType; + isKeyConverter = typeof(TItem).IsAssignableFrom(WellKnownOrmTypes.Key); + BuildConverter(sourceExpression); } } } diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/ExpressionMaterializer.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/ExpressionMaterializer.cs index fdc40eb55c..16696a7374 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/ExpressionMaterializer.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/ExpressionMaterializer.cs @@ -230,7 +230,7 @@ protected override Expression VisitFieldExpression(FieldExpression expression) protected override Expression VisitLocalCollectionExpression(LocalCollectionExpression expression) => throw new NotSupportedException( - string.Format(Strings.ExUnableToMaterializeBackLocalCollectionItem, expression.SourceExpression)); + string.Format(Strings.ExUnableToMaterializeBackLocalCollectionItem, expression.ToString())); protected override Expression VisitStructureFieldExpression(StructureFieldExpression expression) { diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs index bae3460dbb..dbe41009a7 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2021 Xtensive LLC. +// Copyright (C) 2009-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexis Kochetov diff --git a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index 9f54966fc1..0fb070c95e 100644 --- a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs +++ b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2020 Xtensive LLC. +// Copyright (C) 2007-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Nick Svetlov @@ -42,47 +42,42 @@ public int GetHashCode((Type, Type[]) obj) } } - private const string invokeMethodName = "Invoke"; - - private static readonly ConcurrentDictionary<(Type, Type[]), ConstructorInfo> constructorInfoByTypes = - new ConcurrentDictionary<(Type, Type[]), ConstructorInfo>(new TypesEqualityComparer()); + private const string InvokeMethodName = "Invoke"; private static readonly object EmitLock = new object(); private static readonly int NullableTypeMetadataToken = WellKnownTypes.NullableOfT.MetadataToken; - private static readonly Module NullableTypeModule = WellKnownTypes.NullableOfT.Module; + private static readonly int ValueTuple1 = typeof(ValueTuple<>).MetadataToken; + private static readonly int ValueTuple8 = typeof(ValueTuple<,,,,,,,>).MetadataToken; + private static readonly Module SystemCoreLibModule = WellKnownTypes.NullableOfT.Module; private static readonly Type CompilerGeneratedAttributeType = typeof(CompilerGeneratedAttribute); private static readonly string TypeHelperNamespace = typeof(TypeHelper).Namespace; - private static readonly ConcurrentDictionary OrderedInterfaces = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary<(Type, Type[]), ConstructorInfo> ConstructorInfoByTypes = + new(new TypesEqualityComparer()); + + private static readonly ConcurrentDictionary OrderedInterfaces = new(); + + private static readonly ConcurrentDictionary OrderedCompatibles = new(); + + private static readonly ConcurrentDictionary, InterfaceMapping> interfaceMaps = new(); - private static readonly ConcurrentDictionary OrderedCompatibles = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary<(MethodInfo, Type), MethodInfo> GenericMethodInstances1 = new(); - private static readonly ConcurrentDictionary, InterfaceMapping> interfaceMaps = - new ConcurrentDictionary, InterfaceMapping>(); + private static readonly ConcurrentDictionary<(MethodInfo, Type, Type), MethodInfo> GenericMethodInstances2 = new(); - private static readonly ConcurrentDictionary<(MethodInfo, Type), MethodInfo> GenericMethodInstances1 = - new ConcurrentDictionary<(MethodInfo, Type), MethodInfo>(); + private static readonly ConcurrentDictionary<(Type, Type), Type> GenericTypeInstances1 = new(); + + private static readonly ConcurrentDictionary<(Type, Type, Type), Type> GenericTypeInstances2 = new(); private static readonly Func<(MethodInfo genericDefinition, Type typeArgument), MethodInfo> GenericMethodFactory1 = key => key.genericDefinition.MakeGenericMethod(key.typeArgument); - private static readonly ConcurrentDictionary<(MethodInfo, Type, Type), MethodInfo> GenericMethodInstances2 = - new ConcurrentDictionary<(MethodInfo, Type, Type), MethodInfo>(); - private static readonly Func<(MethodInfo genericDefinition, Type typeArgument1, Type typeArgument2), MethodInfo> GenericMethodFactory2 = key => key.genericDefinition.MakeGenericMethod(key.typeArgument1, key.typeArgument2); - private static readonly ConcurrentDictionary<(Type, Type), Type> GenericTypeInstances1 = - new ConcurrentDictionary<(Type, Type), Type>(); - private static readonly Func<(Type genericDefinition, Type typeArgument), Type> GenericTypeFactory1 = key => key.genericDefinition.MakeGenericType(key.typeArgument); - private static readonly ConcurrentDictionary<(Type, Type, Type), Type> GenericTypeInstances2 = - new ConcurrentDictionary<(Type, Type, Type), Type>(); - private static readonly Func<(Type genericDefinition, Type typeArgument1, Type typeArgument2), Type> GenericTypeFactory2 = key => key.genericDefinition.MakeGenericType(key.typeArgument1, key.typeArgument2); @@ -646,7 +641,7 @@ public static ConstructorInfo GetConstructor(this Type type, object[] arguments) GetSingleConstructor(type, arguments.Select(a => a?.GetType()).ToArray()); public static ConstructorInfo GetSingleConstructor(this Type type, Type[] argumentTypes) => - constructorInfoByTypes.GetOrAdd((type, argumentTypes), ConstructorExtractor); + ConstructorInfoByTypes.GetOrAdd((type, argumentTypes), ConstructorExtractor); private static readonly Func<(Type, Type[]), ConstructorInfo> ConstructorExtractor = t => { (var type, var argumentTypes) = t; @@ -899,7 +894,7 @@ private static string GetShortNameBase(this Type type) /// if type is nullable type; /// otherwise, . public static bool IsNullable(this Type type) => - (type.MetadataToken ^ NullableTypeMetadataToken) == 0 && ReferenceEquals(type.Module, NullableTypeModule); + (type.MetadataToken ^ NullableTypeMetadataToken) == 0 && ReferenceEquals(type.Module, SystemCoreLibModule); /// /// Indicates whether type is a type. @@ -931,7 +926,7 @@ public static bool IsNullable(this Type type) => /// /// Type of the delegate to get the "Invoke" method of. /// object describing the delegate "Invoke" method. - public static MethodInfo GetInvokeMethod(this Type delegateType) => delegateType.GetMethod(invokeMethodName); + public static MethodInfo GetInvokeMethod(this Type delegateType) => delegateType.GetMethod(InvokeMethodName); /// @@ -1167,6 +1162,20 @@ public static bool IsNumericType(this Type type) } } + /// + /// Determines whether is generic form of + /// + /// + /// + internal static bool IsValueTuple(this Type type) + { + // this stands on the theory that tokens for all generic versions of ValueTuple + // go one after another. + var currentToken = type.MetadataToken; + return ((currentToken >= ValueTuple1) && currentToken <= ValueTuple8) + && ReferenceEquals(type.Module, SystemCoreLibModule); + } + #region Private \ internal methods /// diff --git a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs index 6c4c876b67..7656b99a1d 100644 --- a/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs +++ b/Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs @@ -74,7 +74,6 @@ internal static class WellKnownTypes public static readonly Type ByteArray = typeof(byte[]); public static readonly Type ObjectArray = typeof(object[]); - public static readonly Type IReadOnlyListOfT = typeof(IReadOnlyList<>); public static readonly Type DefaultMemberAttribute = typeof(DefaultMemberAttribute); }