From 234486d2181024c0637300db5c10e174c60e968f Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Jun 2022 13:21:29 +0500 Subject: [PATCH 01/12] CompiledQueries: Test for closure caching --- .../Linq/CompiledQueriesClosureCachingTest.cs | 1014 +++++++++++++++++ 1 file changed, 1014 insertions(+) create mode 100644 Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs diff --git a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs new file mode 100644 index 0000000000..42d5797592 --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs @@ -0,0 +1,1014 @@ +// Copyright (C) 2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using Xtensive.Orm.Configuration; +using Xtensive.Orm.Tests.Linq.CompiledQueriesClosureCachingTestModel; + +namespace Xtensive.Orm.Tests.Linq.CompiledQueriesClosureCachingTestModel +{ + [Serializable] + [HierarchyRoot] + public class AllTypesNeverCreatedEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public bool FBool { get; set; } + + [Field] + public char FChar { get; set; } + + [Field] + public byte FByte { get; set; } + + [Field] + public sbyte FSByte { get; set; } + + [Field] + public short FShort { get; set; } + + [Field] + public ushort FUShort { get; set; } + + [Field] + public int FInt { get; set; } + + [Field] + public uint FUInt { get; set; } + + [Field] + public long FLong { get; set; } + + [Field] + public ulong FULong { get; set; } + + [Field] + public Guid FGuid { get; set; } + + [Field] + public float FFloat { get; set; } + + [Field] + public double FDouble { get; set; } + + [Field] + public decimal FDecimal { get; set; } + + [Field] + public DateTime FDateTime { get; set; } + + [Field] + public TimeSpan FTimeSpan { get; set; } + + [Field] + public string FString { get; set; } + + [Field] + public string FLongString { get; set; } + + // Nullable fields + + [Field(DefaultValue = true)] + public bool? FNBool { get; set; } + + [Field(DefaultValue = 'x')] + public char? FNChar { get; set; } + + [Field(DefaultValue = byte.MaxValue)] + public byte? FNByte { get; set; } + + [Field(DefaultValue = sbyte.MaxValue)] + public sbyte? FNSByte { get; set; } + + [Field(DefaultValue = short.MaxValue)] + public short? FNShort { get; set; } + + [Field] + public ushort? FNUShort { get; set; } + + [Field] + public int? FNInt { get; set; } + + [Field] + public uint? FNUInt { get; set; } + + [Field] + public long? FNLong { get; set; } + + [Field] // SQLite provides only 8 byte signed integer + public ulong? FNULong { get; set; } + + [Field] + public Guid? FNGuid { get; set; } + + [Field] + public float? FNFloat { get; set; } + + [Field] + public double? FNDouble { get; set; } + + [Field] + public decimal? FNDecimal { get; set; } + + [Field] + public DateTime? FNDateTime { get; set; } + + [Field] + public TimeSpan? FNTimeSpan { get; set; } + } + + [Serializable] + [HierarchyRoot] + public class DateTimeOffsetNeverCreatedEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public DateTimeOffset FDateTimeOffset { get; set; } + + [Field] + public DateTimeOffset? FNDateTimeOffset { get; set; } + } + + [Serializable] + [HierarchyRoot] + public class PgSqlTypesNeverCreatedEntity : Entity + { + [Field, Key] + public int Id { get; private set; } + + [Field] + public NpgsqlTypes.NpgsqlPoint FPoint { get; set; } + + [Field] + public NpgsqlTypes.NpgsqlPoint? FNPoint { get; set; } + } +} + +namespace Xtensive.Orm.Tests.Linq +{ + public sealed class CompiledQueriesClosureCachingTest : AutoBuildTest + { + protected override DomainConfiguration BuildConfiguration() + { + var config = base.BuildConfiguration(); + config.Types.Register(typeof(AllTypesNeverCreatedEntity)); + + if (StorageProviderInfo.Instance.CheckAllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset)) + config.Types.Register(typeof(DateTimeOffsetNeverCreatedEntity)); + if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.PostgreSql)) + config.Types.Register(typeof(PgSqlTypesNeverCreatedEntity)); + + config.UpgradeMode = DomainUpgradeMode.Recreate; + return config; + } + + protected override void CheckRequirements() => Require.ProviderIsNot(StorageProvider.Sqlite); + + [Test] + public void CachingClosureWithBoolean() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, true); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (bool?) true); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithBooleanAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, true); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (bool?) true); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithByte() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (byte) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (byte?) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithByteAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (byte) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (byte?) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithSByte() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (sbyte) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (sbyte?) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithSByteAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (sbyte) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (sbyte?) 127); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithInt16() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (short) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (short?) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithInt16Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = Domain.OpenSession(); + await using var tx = session.OpenTransaction(); + await QueryAsync(session, (short) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (short?) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithUInt16() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (ushort) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (ushort?) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithUInt16Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = Domain.OpenSession(); + await using var tx = session.OpenTransaction(); + await QueryAsync(session, (ushort) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (ushort?) 256); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithInt32() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (int?) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithInt32Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (int?) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithUInt32() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (uint) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (uint?) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithUInt32Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (uint) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (uint?) 512); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithInt64() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (long) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (long?) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithInt64Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (long) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (long?) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithUInt64() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (ulong) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (ulong?) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithUInt64Async() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (ulong) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (ulong?) 1024); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithSingle() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, 1024.1f); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (float?) 1024.1f); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithSingleAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, 1024.1f); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (float?) 1024.1f); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithDouble() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, 1024.2); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (double?) 1024.2); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithDoubleAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, 1024.2); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (double?) 1024.2); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithDecimal() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (decimal) 1024.3); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (decimal?) 1024.3); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithDecimalAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (decimal) 1024.3); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (decimal?) 1024.3); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithChar() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, 'c'); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (char?) 'c'); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithCharAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, 'c'); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (char?) 'c'); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithString() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, "string"); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public async Task CachingClosureWithStringAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = Domain.OpenSession(); + await using var tx = session.OpenTransaction(); + await QueryAsync(session, "string"); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public void CachingClosureWithDateTime() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, DateTime.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (DateTime?) DateTime.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithDateTimeAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, DateTime.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (DateTime?) DateTime.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithTimeSpan() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, TimeSpan.Zero); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (TimeSpan?) TimeSpan.Zero); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithTimeSpanAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, TimeSpan.Zero); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (TimeSpan?) TimeSpan.Zero); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithGuid() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, Guid.NewGuid()); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (Guid?) Guid.NewGuid()); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithGuidAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, Guid.NewGuid()); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (Guid?) Guid.NewGuid()); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public void CachingClosureWithDateTimeOffset() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); + + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, DateTimeOffset.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (DateTimeOffset?) DateTimeOffset.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithDateTimeOffsetAsync() + { + Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); + + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, DateTimeOffset.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (DateTimeOffset?) DateTimeOffset.Now); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + + [Test] + public void CachingClosureWithPgSqlTypes() + { + Require.ProviderIs(StorageProvider.PostgreSql); + + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, new NpgsqlTypes.NpgsqlPoint(0, 0)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, (NpgsqlTypes.NpgsqlPoint?) new NpgsqlTypes.NpgsqlPoint(0, 0)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CachingClosureWithPgSqlTypesAsync() + { + Require.ProviderIs(StorageProvider.PostgreSql); + + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, new NpgsqlTypes.NpgsqlPoint(0, 0)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, (NpgsqlTypes.NpgsqlPoint?) new NpgsqlTypes.NpgsqlPoint(0, 0)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + #region General types + + private void Query(Session session, bool value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FBool == value)); + + private async Task QueryAsync(Session session, bool value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FBool == value)); + + private void Query(Session session, bool? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNBool == value)); + + private async Task QueryAsync(Session session, bool? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNBool == value)); + + private void Query(Session session, byte value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FByte == value)); + + private async Task QueryAsync(Session session, byte value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FByte == value)); + + private void Query(Session session, byte? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNByte == value)); + + private async Task QueryAsync(Session session, byte? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNByte == value)); + + private void Query(Session session, sbyte value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FSByte == value)); + + private async Task QueryAsync(Session session, sbyte value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FSByte == value)); + + private void Query(Session session, sbyte? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNSByte == value)); + + private async Task QueryAsync(Session session, sbyte? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNSByte == value)); + + private void Query(Session session, short value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FShort == value)); + + private async Task QueryAsync(Session session, short value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FShort == value)); + + private void Query(Session session, short? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNShort == value)); + + private async Task QueryAsync(Session session, short? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNShort == value)); + + private void Query(Session session, ushort value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FUShort == value)); + + private async Task QueryAsync(Session session, ushort value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FUShort == value)); + + private void Query(Session session, ushort? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNUShort == value)); + + private async Task QueryAsync(Session session, ushort? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNUShort == value)); + + private void Query(Session session, int value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value)); + + private async Task QueryAsync(Session session, int value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value)); + + private void Query(Session session, int? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNInt == value)); + + private async Task QueryAsync(Session session, int? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNInt == value)); + + private void Query(Session session, uint value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FUInt == value)); + + private async Task QueryAsync(Session session, uint value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FUInt == value)); + + private void Query(Session session, uint? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNUInt == value)); + + private async Task QueryAsync(Session session, uint? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNUInt == value)); + + private void Query(Session session, long value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FLong == value)); + + private async Task QueryAsync(Session session, long value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FLong == value)); + + private void Query(Session session, long? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNLong == value)); + + private async Task QueryAsync(Session session, long? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNLong == value)); + + private void Query(Session session, ulong value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FULong == value)); + + private static async Task QueryAsync(Session session, ulong value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FULong == value)); + + private void Query(Session session, ulong? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNULong == value)); + + private static async Task QueryAsync(Session session, ulong? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNULong == value)); + + private void Query(Session session, float value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FFloat == value)); + + private async Task QueryAsync(Session session, float value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FFloat == value)); + + private void Query(Session session, float? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNFloat == value)); + + private async Task QueryAsync(Session session, float? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNFloat == value)); + + private void Query(Session session, double value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FDouble == value)); + + private async Task QueryAsync(Session session, double value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDouble == value)); + + private void Query(Session session, double? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNDouble == value)); + + private async Task QueryAsync(Session session, double? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDouble == value)); + + private void Query(Session session, decimal value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FDecimal == value)); + + private async Task QueryAsync(Session session, decimal value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDecimal == value)); + + private void Query(Session session, decimal? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNDecimal == value)); + + private async Task QueryAsync(Session session, decimal? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDecimal == value)); + + private void Query(Session session, char value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FChar == value)); + + private async Task QueryAsync(Session session, char value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FChar == value)); + + private void Query(Session session, char? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNChar == value)); + + private async Task QueryAsync(Session session, char? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNChar == value)); + + private void Query(Session session, string value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FString == value)); + + private async Task QueryAsync(Session session, string value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FString == value)); + + private void Query(Session session, DateTime value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FDateTime == value)); + + private async Task QueryAsync(Session session, DateTime value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDateTime == value)); + + private void Query(Session session, DateTime? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNDateTime == value)); + + private async Task QueryAsync(Session session, DateTime? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDateTime == value)); + + private void Query(Session session, TimeSpan value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FTimeSpan == value)); + + private async Task QueryAsync(Session session, TimeSpan value) => + await session.Query.ExecuteAsync(q => q.All().Where(e => e.FTimeSpan == value)); + + private void Query(Session session, TimeSpan? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNTimeSpan == value)); + + private async Task QueryAsync(Session session, TimeSpan? value) => + await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNTimeSpan == value)); + + private void Query(Session session, Guid value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FGuid == value)); + + private async Task QueryAsync(Session session, Guid value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FGuid == value)); + + private void Query(Session session, Guid? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNGuid == value)); + + private async Task QueryAsync(Session session, Guid? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNGuid == value)); + + #endregion + + #region Provider-specific types + + private void Query(Session session, DateTimeOffset value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FDateTimeOffset == value)); + + private async Task QueryAsync(Session session, DateTimeOffset value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDateTimeOffset == value)); + + private void Query(Session session, DateTimeOffset? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNDateTimeOffset == value)); + + private async Task QueryAsync(Session session, DateTimeOffset? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDateTimeOffset == value)); + + private void Query(Session session, NpgsqlTypes.NpgsqlPoint value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FPoint == value)); + + private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FPoint == value)); + + private void Query(Session session, NpgsqlTypes.NpgsqlPoint? value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FNPoint == value)); + + private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint? value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNPoint == value)); + + #endregion + } +} From 7b35e7a8a4256111e5e3f02c23aad33f2dee1bc6 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Jun 2022 17:42:46 +0500 Subject: [PATCH 02/12] More tests --- .../Linq/CompiledQueriesClosureCachingTest.cs | 106 +++++++++++------- 1 file changed, 68 insertions(+), 38 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs index 42d5797592..de99cd796b 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs @@ -173,7 +173,7 @@ protected override DomainConfiguration BuildConfiguration() protected override void CheckRequirements() => Require.ProviderIsNot(StorageProvider.Sqlite); [Test] - public void CachingClosureWithBoolean() + public void BooleanTest() { var countBefore = Domain.QueryCache.Count; @@ -189,7 +189,7 @@ public void CachingClosureWithBoolean() } [Test] - public async Task CachingClosureWithBooleanAsync() + public async Task BooleanTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -205,7 +205,7 @@ public async Task CachingClosureWithBooleanAsync() } [Test] - public void CachingClosureWithByte() + public void ByteTest() { var countBefore = Domain.QueryCache.Count; @@ -221,7 +221,7 @@ public void CachingClosureWithByte() } [Test] - public async Task CachingClosureWithByteAsync() + public async Task ByteTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -237,7 +237,7 @@ public async Task CachingClosureWithByteAsync() } [Test] - public void CachingClosureWithSByte() + public void SByteTest() { var countBefore = Domain.QueryCache.Count; @@ -253,7 +253,7 @@ public void CachingClosureWithSByte() } [Test] - public async Task CachingClosureWithSByteAsync() + public async Task SByteTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -269,7 +269,7 @@ public async Task CachingClosureWithSByteAsync() } [Test] - public void CachingClosureWithInt16() + public void Int16Test() { var countBefore = Domain.QueryCache.Count; @@ -285,7 +285,7 @@ public void CachingClosureWithInt16() } [Test] - public async Task CachingClosureWithInt16Async() + public async Task Int16TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -301,7 +301,7 @@ public async Task CachingClosureWithInt16Async() } [Test] - public void CachingClosureWithUInt16() + public void UInt16Test() { var countBefore = Domain.QueryCache.Count; @@ -317,7 +317,7 @@ public void CachingClosureWithUInt16() } [Test] - public async Task CachingClosureWithUInt16Async() + public async Task UInt16TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -333,7 +333,7 @@ public async Task CachingClosureWithUInt16Async() } [Test] - public void CachingClosureWithInt32() + public void Int32Test() { var countBefore = Domain.QueryCache.Count; @@ -349,7 +349,7 @@ public void CachingClosureWithInt32() } [Test] - public async Task CachingClosureWithInt32Async() + public async Task Int32TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -365,7 +365,7 @@ public async Task CachingClosureWithInt32Async() } [Test] - public void CachingClosureWithUInt32() + public void UInt32Test() { var countBefore = Domain.QueryCache.Count; @@ -381,7 +381,7 @@ public void CachingClosureWithUInt32() } [Test] - public async Task CachingClosureWithUInt32Async() + public async Task UInt32TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -397,7 +397,7 @@ public async Task CachingClosureWithUInt32Async() } [Test] - public void CachingClosureWithInt64() + public void Int64Test() { var countBefore = Domain.QueryCache.Count; @@ -413,7 +413,7 @@ public void CachingClosureWithInt64() } [Test] - public async Task CachingClosureWithInt64Async() + public async Task Int64TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -429,7 +429,7 @@ public async Task CachingClosureWithInt64Async() } [Test] - public void CachingClosureWithUInt64() + public void UInt64Test() { var countBefore = Domain.QueryCache.Count; @@ -445,7 +445,7 @@ public void CachingClosureWithUInt64() } [Test] - public async Task CachingClosureWithUInt64Async() + public async Task UInt64TestAsync() { var countBefore = Domain.QueryCache.Count; @@ -461,7 +461,7 @@ public async Task CachingClosureWithUInt64Async() } [Test] - public void CachingClosureWithSingle() + public void SingleTest() { var countBefore = Domain.QueryCache.Count; @@ -477,7 +477,7 @@ public void CachingClosureWithSingle() } [Test] - public async Task CachingClosureWithSingleAsync() + public async Task SingleTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -493,7 +493,7 @@ public async Task CachingClosureWithSingleAsync() } [Test] - public void CachingClosureWithDouble() + public void DoubleTest() { var countBefore = Domain.QueryCache.Count; @@ -509,7 +509,7 @@ public void CachingClosureWithDouble() } [Test] - public async Task CachingClosureWithDoubleAsync() + public async Task DoubleTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -525,7 +525,7 @@ public async Task CachingClosureWithDoubleAsync() } [Test] - public void CachingClosureWithDecimal() + public void DecimalTest() { var countBefore = Domain.QueryCache.Count; @@ -541,7 +541,7 @@ public void CachingClosureWithDecimal() } [Test] - public async Task CachingClosureWithDecimalAsync() + public async Task DecimalTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -557,7 +557,7 @@ public async Task CachingClosureWithDecimalAsync() } [Test] - public void CachingClosureWithChar() + public void CharTest() { var countBefore = Domain.QueryCache.Count; @@ -573,7 +573,7 @@ public void CachingClosureWithChar() } [Test] - public async Task CachingClosureWithCharAsync() + public async Task CharTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -589,7 +589,7 @@ public async Task CachingClosureWithCharAsync() } [Test] - public void CachingClosureWithString() + public void StringTest() { var countBefore = Domain.QueryCache.Count; @@ -601,7 +601,7 @@ public void CachingClosureWithString() } [Test] - public async Task CachingClosureWithStringAsync() + public async Task StringTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -613,7 +613,7 @@ public async Task CachingClosureWithStringAsync() } [Test] - public void CachingClosureWithDateTime() + public void DateTimeTest() { var countBefore = Domain.QueryCache.Count; @@ -629,7 +629,7 @@ public void CachingClosureWithDateTime() } [Test] - public async Task CachingClosureWithDateTimeAsync() + public async Task DateTimeAsync() { var countBefore = Domain.QueryCache.Count; @@ -645,7 +645,7 @@ public async Task CachingClosureWithDateTimeAsync() } [Test] - public void CachingClosureWithTimeSpan() + public void TimeSpanTest() { var countBefore = Domain.QueryCache.Count; @@ -661,7 +661,7 @@ public void CachingClosureWithTimeSpan() } [Test] - public async Task CachingClosureWithTimeSpanAsync() + public async Task TimeSpanTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -677,7 +677,7 @@ public async Task CachingClosureWithTimeSpanAsync() } [Test] - public void CachingClosureWithGuid() + public void GuidTest() { var countBefore = Domain.QueryCache.Count; @@ -693,7 +693,7 @@ public void CachingClosureWithGuid() } [Test] - public async Task CachingClosureWithGuidAsync() + public async Task GuidTestAsync() { var countBefore = Domain.QueryCache.Count; @@ -709,7 +709,7 @@ public async Task CachingClosureWithGuidAsync() } [Test] - public void CachingClosureWithDateTimeOffset() + public void DateTimeOffsetTest() { Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); @@ -727,7 +727,7 @@ public void CachingClosureWithDateTimeOffset() } [Test] - public async Task CachingClosureWithDateTimeOffsetAsync() + public async Task DateTimeOffsetTestAsync() { Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); @@ -746,7 +746,7 @@ public async Task CachingClosureWithDateTimeOffsetAsync() [Test] - public void CachingClosureWithPgSqlTypes() + public void PgSqlTypesTest() { Require.ProviderIs(StorageProvider.PostgreSql); @@ -764,7 +764,7 @@ public void CachingClosureWithPgSqlTypes() } [Test] - public async Task CachingClosureWithPgSqlTypesAsync() + public async Task PgSqlTypesTestAsync() { Require.ProviderIs(StorageProvider.PostgreSql); @@ -781,6 +781,30 @@ public async Task CachingClosureWithPgSqlTypesAsync() Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); } + [Test] + public void ValueTupleTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, (1, 1.2f)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public async Task ValueTupleTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, (1, 1.2f)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + #region General types private void Query(Session session, bool value) => @@ -981,6 +1005,12 @@ private void Query(Session session, Guid? value) => private async Task QueryAsync(Session session, Guid? value) => _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNGuid == value)); + private void Query(Session session, (int value1, float value2) value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.value1 || e.FFloat == value.value2)); + + private async Task QueryAsync(Session session, (int value1, float value2) value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.value1 || e.FFloat == value.value2)); + #endregion #region Provider-specific types From 5b32fc4f4c74eaf149a5320532baf6062e0b4b85 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 20 Jun 2022 13:25:02 +0500 Subject: [PATCH 03/12] CompiledQueryRunner: Improved check whether query is cacheable --- .../Orm/Internals/CompiledQueryRunner.cs | 52 ++++++++++++---- Orm/Xtensive.Orm/Reflection/TypeHelper.cs | 59 +++++++++++-------- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index e499166a64..1e6b2a4166 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,7 +21,8 @@ namespace Xtensive.Orm.Internals { internal class CompiledQueryRunner { - private static readonly Func FieldIsSimple = fieldInfo => IsSimpleType(fieldInfo.FieldType); + private static readonly Func, bool> IsFieldReadyToCache = (fieldInfo, supportedTypes) => + IsTypeCacheable(fieldInfo.FieldType, supportedTypes); private readonly Domain domain; private readonly Session session; @@ -28,6 +30,7 @@ internal class CompiledQueryRunner private readonly object queryKey; private readonly object queryTarget; private readonly ParameterContext outerContext; + private readonly IReadOnlySet supportedTypes; private Parameter queryParameter; private ExtendedExpressionReplacer queryParameterReplacer; @@ -200,23 +203,49 @@ private bool AllocateParameterAndReplacer() return null; }); + return !TypeHelper.IsClosure(closureType) - || closureType.GetFields().All(FieldIsSimple); + || closureType.GetFields().All(f => IsFieldReadyToCache(f, supportedTypes)); } - private static bool IsSimpleType(Type type) + private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes) { - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsGenericType) { - var genericDef = typeInfo.GetGenericTypeDefinition(); - return (genericDef == WellKnownTypes.NullableOfT || genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT)) - && IsSimpleType(typeInfo.GetGenericArguments()[0]); + var type1 = type.StripNullable(); + if (type1.IsGenericType) { + // IReadOnlyList implementations + ValueTuple<> with different number of argument types + if (type1.IsValueTuple() && type1.GetGenericArguments().All(t => IsTypeCacheable(t, supportedTypes))) { + return true; + } + var genericDef = type1.GetGenericTypeDefinition(); + return genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT) && IsTypeCacheable(type1.GetGenericArguments()[0], supportedTypes); } - else if (typeInfo.IsArray) { - return IsSimpleType(typeInfo.GetElementType()); + else if (type1.IsArray) { + return IsTypeCacheable(type1.GetElementType(), supportedTypes); } else { - return typeInfo.IsPrimitive || typeInfo.IsEnum || type == WellKnownTypes.String || type == WellKnownTypes.Decimal; + // enums are handled by their base type so no need to check them + return Type.GetTypeCode(type1) switch { + TypeCode.Boolean => true, + TypeCode.Byte => true, + TypeCode.SByte => true, + TypeCode.Int16 => true, + TypeCode.UInt16 => true, + TypeCode.Int32 => true, + TypeCode.UInt32 => true, + TypeCode.Int64 => true, + TypeCode.UInt64 => true, + TypeCode.Single => true, + TypeCode.Double => true, + TypeCode.Decimal => true, + TypeCode.Char => true, + TypeCode.String => true, + TypeCode.DateTime => true, + TypeCode.Object => type1 == WellKnownTypes.Guid + || type1 == WellKnownTypes.TimeSpan + || type1 == WellKnownTypes.DateTimeOffset + || supportedTypes.Contains(type1), + _ => false + }; } } @@ -245,6 +274,7 @@ public CompiledQueryRunner(QueryEndpoint endpoint, object queryKey, object query this.queryKey = new Pair(queryKey, session.StorageNodeId); this.queryTarget = queryTarget; this.outerContext = outerContext; + supportedTypes = domain.StorageProviderInfo.SupportedTypes; } } } diff --git a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index 9f54966fc1..c814e3cb17 100644 --- a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs +++ b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs @@ -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 /// From dfa3d02306f9baf6212294051250873da823625c Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 21 Jun 2022 19:07:38 +0500 Subject: [PATCH 04/12] Some refactoring - AllocateParameterAndReplacer method returns back its responsibility declared in name - Checking for ability to cache query is moved to method that caches query - No two calls for queryTarget.GetType(), it is cached when AllocateParameterAndReplacer method is called, since the method called only when caching is needed we can use cached type later on in IsQueryCacheable method - all structs allowed in closure type --- .../Orm/Internals/CompiledQueryRunner.cs | 63 ++++++++++++------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index 1e6b2a4166..be3527eba1 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -21,9 +21,6 @@ namespace Xtensive.Orm.Internals { internal class CompiledQueryRunner { - private static readonly Func, bool> IsFieldReadyToCache = (fieldInfo, supportedTypes) => - IsTypeCacheable(fieldInfo.FieldType, supportedTypes); - private readonly Domain domain; private readonly Session session; private readonly QueryEndpoint endpoint; @@ -34,6 +31,7 @@ internal class CompiledQueryRunner private Parameter queryParameter; private ExtendedExpressionReplacer queryParameterReplacer; + private Type queryTargetType; public QueryResult ExecuteCompiled(Func> query) { @@ -114,24 +112,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); - } + PutQueryToCacheIfAllowed(parameterizedQuery); + return parameterizedQuery; } @@ -143,7 +141,7 @@ private ParameterizedQuery GetSequenceQuery( return parameterizedQuery; } - var cacheable = AllocateParameterAndReplacer(); + AllocateParameterAndReplacer(); var scope = new CompiledQueryProcessingScope(queryParameter, queryParameterReplacer); using (scope.Enter()) { var result = query.Invoke(endpoint); @@ -151,21 +149,20 @@ private ParameterizedQuery GetSequenceQuery( parameterizedQuery = (ParameterizedQuery) translatedQuery; } - if (cacheable) { - PutCachedQuery(parameterizedQuery); - } + PutQueryToCacheIfAllowed(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(); + var closureType = queryTargetType = queryTarget.GetType(); var parameterType = WellKnownOrmTypes.ParameterOfT.CachedMakeGenericType(closureType); var valueMemberInfo = parameterType.GetProperty(nameof(Parameter.Value), closureType); queryParameter = (Parameter) System.Activator.CreateInstance(parameterType, "pClosure"); @@ -202,10 +199,24 @@ private bool AllocateParameterAndReplacer() } return null; }); + } + + private bool IsQueryCacheable() + { + if (queryTargetType==null) { + return true; + } + if (!queryTargetType.IsClosure()) { + return true; + } - return !TypeHelper.IsClosure(closureType) - || closureType.GetFields().All(f => IsFieldReadyToCache(f, supportedTypes)); + foreach (var field in queryTargetType.GetFields()) { + if (!IsTypeCacheable(field.FieldType, supportedTypes)) { + return false; + } + } + return true; } private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes) @@ -240,10 +251,7 @@ private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes TypeCode.Char => true, TypeCode.String => true, TypeCode.DateTime => true, - TypeCode.Object => type1 == WellKnownTypes.Guid - || type1 == WellKnownTypes.TimeSpan - || type1 == WellKnownTypes.DateTimeOffset - || supportedTypes.Contains(type1), + TypeCode.Object => type1.IsValueType, _ => false }; } @@ -252,8 +260,17 @@ private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes private ParameterizedQuery GetCachedQuery() => domain.QueryCache.TryGetItem(queryKey, true, out var item) ? item.Second : null; - private void PutCachedQuery(ParameterizedQuery parameterizedQuery) => - domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); + private void PutQueryToCacheIfAllowed(ParameterizedQuery parameterizedQuery) { + if (IsQueryCacheable()) { + domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); + } + else { + // no .resx used because it is hot path. + if (OrmLog.IsLogged(Logging.LogLevel.Info)) + OrmLog.Info("Query can't be cached because closure type it has references to captures reference" + + " type instances. This will lead to long-living objects in memory."); + } + } private ParameterContext CreateParameterContext(ParameterizedQuery query) { From 06fbf1388b0e009ec8744bfe70feceead48974d0 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 21 Jun 2022 19:10:17 +0500 Subject: [PATCH 05/12] Tests for custom value types + minor changes of test enitites --- .../Linq/CompiledQueriesClosureCachingTest.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs index de99cd796b..59fcac47e1 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs @@ -121,6 +121,11 @@ public class AllTypesNeverCreatedEntity : Entity [Field] public TimeSpan? FNTimeSpan { get; set; } + + public AllTypesNeverCreatedEntity(Session session) + : base(session) + { + } } [Serializable] @@ -135,6 +140,11 @@ public class DateTimeOffsetNeverCreatedEntity : Entity [Field] public DateTimeOffset? FNDateTimeOffset { get; set; } + + public DateTimeOffsetNeverCreatedEntity(Session session) + : base(session) + { + } } [Serializable] @@ -149,6 +159,39 @@ public class PgSqlTypesNeverCreatedEntity : Entity [Field] public NpgsqlTypes.NpgsqlPoint? FNPoint { get; set; } + + public PgSqlTypesNeverCreatedEntity(Session session) + : base(session) + { + } + } + + public struct PropsStruct + { + public int IntField { get; set; } + + public long LongField { get; set; } + } + + public struct PropsWrongStruct + { + public int IntField { get; set; } + + public Tuple TupleField { get; set; } + } + + public struct FieldsStruct + { + public int IntField; + + public long LongField; + } + + public struct FieldsWrongStruct + { + public int IntField; + + public Tuple TupleField; } } @@ -805,6 +848,38 @@ public async Task ValueTupleTestAsync() Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); } + [Test] + public void CustomStructTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, new PropsStruct() { IntField = 10, LongField = 100 }); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + Query(session, new FieldsStruct() { IntField = 10, LongField = 100 }); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + + [Test] + public async Task CustomStructTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, new PropsStruct() { IntField = 10, LongField = 100 }); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + + await QueryAsync(session, new FieldsStruct() { IntField = 10, LongField = 100 }); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); + } + #region General types private void Query(Session session, bool value) => @@ -1039,6 +1114,19 @@ private void Query(Session session, NpgsqlTypes.NpgsqlPoint? value) => private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint? value) => _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNPoint == value)); + + private void Query(Session session, PropsStruct value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + + private async Task QueryAsync(Session session, PropsStruct value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + + private void Query(Session session, FieldsStruct value) => + _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + + private async Task QueryAsync(Session session, FieldsStruct value) => + _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + #endregion } } From e171fe777492cebede955ff1a3a0858fc9e5d65a Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 22 Jun 2022 13:37:36 +0500 Subject: [PATCH 06/12] Return TypeInfo usage + no closure within IsTypeCacheable --- .../Orm/Internals/CompiledQueryRunner.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index be3527eba1..dce5aa5536 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -222,15 +222,21 @@ private bool IsQueryCacheable() private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes) { var type1 = type.StripNullable(); - if (type1.IsGenericType) { - // IReadOnlyList implementations + ValueTuple<> with different number of argument types - if (type1.IsValueTuple() && type1.GetGenericArguments().All(t => IsTypeCacheable(t, supportedTypes))) { + var typeInfo = type1.GetTypeInfo(); + if (typeInfo.IsGenericType) { + // IReadOnlyList implementations + ValueTuple<> with different number of type arguments + if (type1.IsValueTuple()) { + foreach (var arg in typeInfo.GenericTypeArguments) { + if (!IsTypeCacheable(arg, supportedTypes)) { + return false; + } + } return true; } - var genericDef = type1.GetGenericTypeDefinition(); - return genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT) && IsTypeCacheable(type1.GetGenericArguments()[0], supportedTypes); + var genericDef = typeInfo.GetGenericTypeDefinition(); + return genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT) && IsTypeCacheable(typeInfo.GetGenericArguments()[0], supportedTypes); } - else if (type1.IsArray) { + else if (typeInfo.IsArray) { return IsTypeCacheable(type1.GetElementType(), supportedTypes); } else { @@ -251,7 +257,7 @@ private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes TypeCode.Char => true, TypeCode.String => true, TypeCode.DateTime => true, - TypeCode.Object => type1.IsValueType, + TypeCode.Object => typeInfo.IsValueType, _ => false }; } From 709a08380448b2babbf794fc13da9a95fa199dc9 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 22 Jun 2022 15:45:06 +0500 Subject: [PATCH 07/12] Test for TypeHelper.IsValueTuple() --- .../Reflection/TypeHelperTest.cs | 49 +++++++++++++++++-- Orm/Xtensive.Orm/Reflection/TypeHelper.cs | 2 +- 2 files changed, 47 insertions(+), 4 deletions(-) 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/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index c814e3cb17..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 From 5ed850e45d692fbabbb0ceebd8ac4a64578f6197 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 23 Jun 2022 20:12:52 +0500 Subject: [PATCH 08/12] Trigger closure check only if query uses local collections As I see local collections trigger ItemToTupleConverter to be built and closure type instance can be captured within ParameterizedQuery. --- .../Orm/Internals/CompiledQueryProcessingScope.cs | 3 ++- .../Orm/Internals/CompiledQueryRunner.cs | 13 ++++++------- Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs index fa0dde9aeb..a7d53f7a90 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 @@ -19,6 +19,7 @@ internal sealed class CompiledQueryProcessingScope public ExtendedExpressionReplacer QueryParameterReplacer { get; } public ParameterContext ParameterContext { get; } public bool Execute { get; } + public bool CheckIfCacheble { get; set; } = false; [field: ThreadStatic] internal static CompiledQueryProcessingScope Current { get; private set; } diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index dce5aa5536..98f8d95cac 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -128,7 +128,7 @@ private ParameterizedQuery GetScalarQuery( throw new NotSupportedException(Strings.ExNonLinqCallsAreNotSupportedWithinQueryExecuteDelayed); } - PutQueryToCacheIfAllowed(parameterizedQuery); + PutQueryToCacheIfAllowed(parameterizedQuery, scope.CheckIfCacheble); return parameterizedQuery; } @@ -149,7 +149,7 @@ private ParameterizedQuery GetSequenceQuery( parameterizedQuery = (ParameterizedQuery) translatedQuery; } - PutQueryToCacheIfAllowed(parameterizedQuery); + PutQueryToCacheIfAllowed(parameterizedQuery, scope.CheckIfCacheble); return parameterizedQuery; } @@ -266,16 +266,15 @@ private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes private ParameterizedQuery GetCachedQuery() => domain.QueryCache.TryGetItem(queryKey, true, out var item) ? item.Second : null; - private void PutQueryToCacheIfAllowed(ParameterizedQuery parameterizedQuery) { - if (IsQueryCacheable()) { - domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); - } - else { + private void PutQueryToCacheIfAllowed(ParameterizedQuery parameterizedQuery, in bool checkIfCacheable) { + if (checkIfCacheable && !IsQueryCacheable()) { // no .resx used because it is hot path. if (OrmLog.IsLogged(Logging.LogLevel.Info)) OrmLog.Info("Query can't be cached because closure type it has references to captures reference" + " type instances. This will lead to long-living objects in memory."); + return; } + domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); } private ParameterContext CreateParameterContext(ParameterizedQuery query) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs index bae3460dbb..1895e243e6 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 @@ -1696,6 +1696,7 @@ private ProjectionExpression VisitLocalCollectionSequence(Expression sequ if (compiledQueryScope != null) { var replacer = compiledQueryScope.QueryParameterReplacer; var replace = replacer.Replace(sequence); + compiledQueryScope.CheckIfCacheble = true; var parameter = ParameterAccessorFactory.CreateAccessorExpression>(replace); collectionGetter = parameter.CachingCompile(); } From b40b2e26f57209a20ea501d98c56d935c36a02f6 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 23 Jun 2022 20:13:36 +0500 Subject: [PATCH 09/12] Additional tests for the github issue 224 --- .../IssueGithub0224_DelayedQueryCapture.cs | 138 ++++++++++++++++-- 1 file changed, 123 insertions(+), 15 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs index 47a71904d9..f0f8d9e66c 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,139 @@ 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() + { + var cachedItems = Domain.QueryCache.Count; + 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); + Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems)); + } + + [Test] + public void DelayedQueryWithContainsTest() + { + var cachedItems = Domain.QueryCache.Count; + 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); + Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems)); + } + + [Test] + public void DelayedQueryWithEqualityTest() { + var cachedItems = Domain.QueryCache.Count; 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); + Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems + 1)); + } + + 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 +179,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 + }, + }); + } } } From 091946148ee8ac0ec8add49f449f4acd62cd3a49 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 24 Jun 2022 16:10:44 +0500 Subject: [PATCH 10/12] Tests are changed to have local collections in queries --- .../Linq/CompiledQueriesClosureCachingTest.cs | 840 ++++++++++++++---- 1 file changed, 678 insertions(+), 162 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs index 59fcac47e1..22dc8da8a0 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs @@ -215,6 +215,109 @@ protected override DomainConfiguration BuildConfiguration() protected override void CheckRequirements() => Require.ProviderIsNot(StorageProvider.Sqlite); + public static Tuple StaticField; + public Tuple Field; + + public static Tuple StaticProperty { get; set; } + public Tuple Property { get; set; } + + + [Test] + public void FieldTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + QueryWithField(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + + [Test] + public async Task FieldTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryWithFieldAsync(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + + [Test] + public void StaticFieldTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + QueryWithStaticField(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public async Task StaticFieldTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryWithStaticFieldAsync(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public void PropertyTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + QueryWithProperty(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + + [Test] + public async Task PropertyTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryWithPropertyAsync(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + + [Test] + public void StaticPropertyTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + QueryWithStaticProperty(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + + [Test] + public async Task StaticPropertyTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryWithStaticPropertyAsync(session); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); + } + [Test] public void BooleanTest() { @@ -880,252 +983,665 @@ public async Task CustomStructTestAsync() Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); } + [Test] + public void UnsupportedTypeTest() + { + var countBefore = Domain.QueryCache.Count; + + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + Query(session, new Tuple(1)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + + [Test] + public async Task UnsupportedTypeTestAsync() + { + var countBefore = Domain.QueryCache.Count; + + await using var session = await Domain.OpenSessionAsync(); + await using var tx = await session.OpenTransactionAsync(); + await QueryAsync(session, new Tuple(1)); + + Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); + } + #region General types - private void Query(Session session, bool value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FBool == value)); + private void QueryWithField(Session session) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == Field.Item1).ToArray(); + } + + private async Task QueryWithFieldAsync(Session session) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == Field.Item1).ToArray(); + } + + private void QueryWithStaticField(Session session) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == StaticField.Item1).ToArray(); + } + + private async Task QueryWithStaticFieldAsync(Session session) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == StaticField.Item1).ToArray(); + } + + private void QueryWithProperty(Session session) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == Property.Item1).ToArray(); + } + + private async Task QueryWithPropertyAsync(Session session) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == Property.Item1).ToArray(); + } + + private void QueryWithStaticProperty(Session session) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == StaticProperty.Item1).ToArray(); + } + + private async Task QueryWithStaticPropertyAsync(Session session) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == StaticProperty.Item1).ToArray(); + } + + private void Query(Session session, Tuple value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.Item1).ToArray(); + } - private async Task QueryAsync(Session session, bool value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FBool == value)); + private async Task QueryAsync(Session session, Tuple value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.Item1).ToArray(); + } + + private void Query(Session session, bool value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FBool == value).ToArray(); + } - private void Query(Session session, bool? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNBool == value)); + private async Task QueryAsync(Session session, bool value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FBool == value).ToArray(); + } - private async Task QueryAsync(Session session, bool? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNBool == value)); + private void Query(Session session, bool? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNBool == value).ToArray(); + } - private void Query(Session session, byte value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FByte == value)); + private async Task QueryAsync(Session session, bool? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNBool == value).ToArray(); + } - private async Task QueryAsync(Session session, byte value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FByte == value)); + private void Query(Session session, byte value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FByte == value).ToArray(); + } - private void Query(Session session, byte? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNByte == value)); + private async Task QueryAsync(Session session, byte value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FByte == value).ToArray(); + } - private async Task QueryAsync(Session session, byte? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNByte == value)); + private void Query(Session session, byte? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNByte == value).ToArray(); + } - private void Query(Session session, sbyte value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FSByte == value)); + private async Task QueryAsync(Session session, byte? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNByte == value).ToArray(); + } - private async Task QueryAsync(Session session, sbyte value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FSByte == value)); + private void Query(Session session, sbyte value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FSByte == value).ToArray(); + } - private void Query(Session session, sbyte? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNSByte == value)); + private async Task QueryAsync(Session session, sbyte value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FSByte == value).ToArray(); + } - private async Task QueryAsync(Session session, sbyte? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNSByte == value)); + private void Query(Session session, sbyte? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNSByte == value).ToArray(); + } - private void Query(Session session, short value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FShort == value)); + private async Task QueryAsync(Session session, sbyte? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNSByte == value).ToArray(); + } - private async Task QueryAsync(Session session, short value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FShort == value)); + private void Query(Session session, short value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FShort == value).ToArray(); + } - private void Query(Session session, short? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNShort == value)); + private async Task QueryAsync(Session session, short value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FShort == value).ToArray(); + } - private async Task QueryAsync(Session session, short? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNShort == value)); + private void Query(Session session, short? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNShort == value).ToArray(); + } - private void Query(Session session, ushort value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FUShort == value)); + private async Task QueryAsync(Session session, short? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNShort == value).ToArray(); + } - private async Task QueryAsync(Session session, ushort value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FUShort == value)); + private void Query(Session session, ushort value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FUShort == value).ToArray(); + } - private void Query(Session session, ushort? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNUShort == value)); + private async Task QueryAsync(Session session, ushort value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FUShort == value).ToArray(); + } - private async Task QueryAsync(Session session, ushort? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNUShort == value)); + private void Query(Session session, ushort? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNUShort == value).ToArray(); + } - private void Query(Session session, int value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value)); + private async Task QueryAsync(Session session, ushort? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNUShort == value).ToArray(); + } - private async Task QueryAsync(Session session, int value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value)); + private void Query(Session session, int value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value).ToArray(); + } - private void Query(Session session, int? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNInt == value)); + private async Task QueryAsync(Session session, int value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value).ToArray(); + } - private async Task QueryAsync(Session session, int? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNInt == value)); + private void Query(Session session, int? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNInt == value).ToArray(); + } - private void Query(Session session, uint value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FUInt == value)); + private async Task QueryAsync(Session session, int? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNInt == value).ToArray(); + } - private async Task QueryAsync(Session session, uint value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FUInt == value)); + private void Query(Session session, uint value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FUInt == value).ToArray(); + } - private void Query(Session session, uint? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNUInt == value)); + private async Task QueryAsync(Session session, uint value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FUInt == value).ToArray(); + } - private async Task QueryAsync(Session session, uint? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNUInt == value)); + private void Query(Session session, uint? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNUInt == value).ToArray(); + } - private void Query(Session session, long value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FLong == value)); + private async Task QueryAsync(Session session, uint? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNUInt == value).ToArray(); + } - private async Task QueryAsync(Session session, long value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FLong == value)); + private void Query(Session session, long value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FLong == value).ToArray(); + } - private void Query(Session session, long? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNLong == value)); + private async Task QueryAsync(Session session, long value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FLong == value).ToArray(); + } - private async Task QueryAsync(Session session, long? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNLong == value)); + private void Query(Session session, long? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNLong == value).ToArray(); + } - private void Query(Session session, ulong value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FULong == value)); + private async Task QueryAsync(Session session, long? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNLong == value).ToArray(); + } - private static async Task QueryAsync(Session session, ulong value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FULong == value)); + private void Query(Session session, ulong value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FULong == value).ToArray(); + } - private void Query(Session session, ulong? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNULong == value)); + private static async Task QueryAsync(Session session, ulong value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FULong == value).ToArray(); + } - private static async Task QueryAsync(Session session, ulong? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNULong == value)); + private void Query(Session session, ulong? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNULong == value).ToArray(); + } - private void Query(Session session, float value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FFloat == value)); + private static async Task QueryAsync(Session session, ulong? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNULong == value).ToArray(); + } - private async Task QueryAsync(Session session, float value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FFloat == value)); + private void Query(Session session, float value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FFloat == value).ToArray(); + } - private void Query(Session session, float? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNFloat == value)); + private async Task QueryAsync(Session session, float value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FFloat == value).ToArray(); + } - private async Task QueryAsync(Session session, float? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNFloat == value)); + private void Query(Session session, float? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNFloat == value).ToArray(); + } - private void Query(Session session, double value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FDouble == value)); + private async Task QueryAsync(Session session, float? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNFloat == value).ToArray(); + } - private async Task QueryAsync(Session session, double value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDouble == value)); + private void Query(Session session, double value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDouble == value).ToArray(); + } - private void Query(Session session, double? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNDouble == value)); + private async Task QueryAsync(Session session, double value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDouble == value).ToArray(); + } - private async Task QueryAsync(Session session, double? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDouble == value)); + private void Query(Session session, double? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDouble == value).ToArray(); + } - private void Query(Session session, decimal value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FDecimal == value)); + private async Task QueryAsync(Session session, double? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDouble == value).ToArray(); + } - private async Task QueryAsync(Session session, decimal value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDecimal == value)); + private void Query(Session session, decimal value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDecimal == value).ToArray(); + } - private void Query(Session session, decimal? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNDecimal == value)); + private async Task QueryAsync(Session session, decimal value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDecimal == value).ToArray(); + } - private async Task QueryAsync(Session session, decimal? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDecimal == value)); + private void Query(Session session, decimal? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDecimal == value).ToArray(); + } - private void Query(Session session, char value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FChar == value)); + private async Task QueryAsync(Session session, decimal? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDecimal == value).ToArray(); + } - private async Task QueryAsync(Session session, char value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FChar == value)); + private void Query(Session session, char value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FChar == value).ToArray(); + } - private void Query(Session session, char? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNChar == value)); + private async Task QueryAsync(Session session, char value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FChar == value).ToArray(); + } - private async Task QueryAsync(Session session, char? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNChar == value)); + private void Query(Session session, char? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNChar == value).ToArray(); + } - private void Query(Session session, string value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FString == value)); + private async Task QueryAsync(Session session, char? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNChar == value).ToArray(); + } - private async Task QueryAsync(Session session, string value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FString == value)); + private void Query(Session session, string value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FString == value).ToArray(); + } - private void Query(Session session, DateTime value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FDateTime == value)); + private async Task QueryAsync(Session session, string value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FString == value).ToArray(); + } - private async Task QueryAsync(Session session, DateTime value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDateTime == value)); + private void Query(Session session, DateTime value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDateTime == value).ToArray(); + } - private void Query(Session session, DateTime? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNDateTime == value)); + private async Task QueryAsync(Session session, DateTime value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDateTime == value).ToArray(); + } - private async Task QueryAsync(Session session, DateTime? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDateTime == value)); + private void Query(Session session, DateTime? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDateTime == value).ToArray(); + } - private void Query(Session session, TimeSpan value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FTimeSpan == value)); + private async Task QueryAsync(Session session, DateTime? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDateTime == value).ToArray(); + } - private async Task QueryAsync(Session session, TimeSpan value) => - await session.Query.ExecuteAsync(q => q.All().Where(e => e.FTimeSpan == value)); + private void Query(Session session, TimeSpan value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FTimeSpan == value).ToArray(); + } - private void Query(Session session, TimeSpan? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNTimeSpan == value)); + private async Task QueryAsync(Session session, TimeSpan value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FTimeSpan == value).ToArray(); + } - private async Task QueryAsync(Session session, TimeSpan? value) => - await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNTimeSpan == value)); + private void Query(Session session, TimeSpan? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNTimeSpan == value).ToArray(); + } - private void Query(Session session, Guid value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FGuid == value)); + private async Task QueryAsync(Session session, TimeSpan? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNTimeSpan == value).ToArray(); + } - private async Task QueryAsync(Session session, Guid value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FGuid == value)); + private void Query(Session session, Guid value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FGuid == value).ToArray(); + } - private void Query(Session session, Guid? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNGuid == value)); + private async Task QueryAsync(Session session, Guid value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FGuid == value).ToArray(); + } - private async Task QueryAsync(Session session, Guid? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNGuid == value)); + private void Query(Session session, Guid? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNGuid == value).ToArray(); + } - private void Query(Session session, (int value1, float value2) value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.value1 || e.FFloat == value.value2)); + private async Task QueryAsync(Session session, Guid? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNGuid == value).ToArray(); + } - private async Task QueryAsync(Session session, (int value1, float value2) value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.value1 || e.FFloat == value.value2)); + private void Query(Session session, (int value1, float value2) value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.value1 || e.FFloat == value.value2).ToArray(); + } - #endregion + private async Task QueryAsync(Session session, (int value1, float value2) value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.value1 || e.FFloat == value.value2).ToArray(); + } - #region Provider-specific types + private void Query(Session session, PropsStruct value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); + } - private void Query(Session session, DateTimeOffset value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FDateTimeOffset == value)); + private async Task QueryAsync(Session session, PropsStruct value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); + } - private async Task QueryAsync(Session session, DateTimeOffset value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FDateTimeOffset == value)); + private void Query(Session session, FieldsStruct value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); + } - private void Query(Session session, DateTimeOffset? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNDateTimeOffset == value)); + private async Task QueryAsync(Session session, FieldsStruct value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); + } - private async Task QueryAsync(Session session, DateTimeOffset? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNDateTimeOffset == value)); + #endregion - private void Query(Session session, NpgsqlTypes.NpgsqlPoint value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FPoint == value)); + #region Provider-specific types - private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FPoint == value)); + private void Query(Session session, DateTimeOffset value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDateTimeOffset == value).ToArray(); + } - private void Query(Session session, NpgsqlTypes.NpgsqlPoint? value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FNPoint == value)); + private async Task QueryAsync(Session session, DateTimeOffset value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FDateTimeOffset == value).ToArray(); + } - private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint? value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FNPoint == value)); + private void Query(Session session, DateTimeOffset? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDateTimeOffset == value).ToArray(); + } + private async Task QueryAsync(Session session, DateTimeOffset? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNDateTimeOffset == value).ToArray(); + } - private void Query(Session session, PropsStruct value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + private void Query(Session session, NpgsqlTypes.NpgsqlPoint value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FPoint == value).ToArray(); + } - private async Task QueryAsync(Session session, PropsStruct value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FPoint == value).ToArray(); + } - private void Query(Session session, FieldsStruct value) => - _ = session.Query.Execute(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + private void Query(Session session, NpgsqlTypes.NpgsqlPoint? value) + { + var ids = new[] { 1, 2 }; + var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNPoint == value).ToArray(); + } - private async Task QueryAsync(Session session, FieldsStruct value) => - _ = await session.Query.ExecuteAsync(q => q.All().Where(e => e.FInt == value.IntField || e.FLong == value.LongField)); + private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint? value) + { + var ids = new[] { 1, 2 }; + var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); + _ = items.Where(e => e.FNPoint == value).ToArray(); + } #endregion } From 039535c806ef091337231880a43a1246211aa1a0 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 24 Jun 2022 16:02:35 +0500 Subject: [PATCH 11/12] Test renamed --- ...s => ConditionalQueryCachingWithLocalCollectionsTest.cs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename Orm/Xtensive.Orm.Tests/Linq/{CompiledQueriesClosureCachingTest.cs => ConditionalQueryCachingWithLocalCollectionsTest.cs} (99%) diff --git a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs b/Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs similarity index 99% rename from Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs rename to Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs index 22dc8da8a0..c7e6e85fc9 100644 --- a/Orm/Xtensive.Orm.Tests/Linq/CompiledQueriesClosureCachingTest.cs +++ b/Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs @@ -7,9 +7,9 @@ using System.Threading.Tasks; using NUnit.Framework; using Xtensive.Orm.Configuration; -using Xtensive.Orm.Tests.Linq.CompiledQueriesClosureCachingTestModel; +using Xtensive.Orm.Tests.Linq.ConditionalQueryCachingWithLocalCollectionsTestModel; -namespace Xtensive.Orm.Tests.Linq.CompiledQueriesClosureCachingTestModel +namespace Xtensive.Orm.Tests.Linq.ConditionalQueryCachingWithLocalCollectionsTestModel { [Serializable] [HierarchyRoot] @@ -197,7 +197,7 @@ public struct FieldsWrongStruct namespace Xtensive.Orm.Tests.Linq { - public sealed class CompiledQueriesClosureCachingTest : AutoBuildTest + public sealed class ConditionalQueryCachingWithLocalCollectionsTest : AutoBuildTest { protected override DomainConfiguration BuildConfiguration() { From f53e8f1ad9498ce949536d39bd763b010a1cca89 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 30 Jun 2022 17:21:06 +0500 Subject: [PATCH 12/12] Solves original problem of closure instance capture Also revert previous changes of conditional caching --- .../IssueGithub0224_DelayedQueryCapture.cs | 6 - ...nalQueryCachingWithLocalCollectionsTest.cs | 1648 ----------------- .../Internals/CompiledQueryProcessingScope.cs | 1 - .../Orm/Internals/CompiledQueryRunner.cs | 81 +- .../Expressions/LocalCollectionExpression.cs | 61 +- .../Orm/Linq/ItemToTupleConverter{TItem}.cs | 78 +- .../Materialization/ExpressionMaterializer.cs | 2 +- .../Orm/Linq/Translator.Queryable.cs | 1 - Orm/Xtensive.Orm/Reflection/WellKnownTypes.cs | 1 - 9 files changed, 77 insertions(+), 1802 deletions(-) delete mode 100644 Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs index f0f8d9e66c..56ae7c26a1 100755 --- a/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs +++ b/Orm/Xtensive.Orm.Tests/Issues/IssueGithub0224_DelayedQueryCapture.cs @@ -98,7 +98,6 @@ protected override DomainConfiguration BuildConfiguration() [Test] public void DelayedQueryWithIncludeTest() { - var cachedItems = Domain.QueryCache.Count; using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var item = new Item() { Tag = 10 }; @@ -107,13 +106,11 @@ public void DelayedQueryWithIncludeTest() } TestHelper.CollectGarbage(true); Assert.AreEqual(0, OtherService1.InstanceCount); - Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems)); } [Test] public void DelayedQueryWithContainsTest() { - var cachedItems = Domain.QueryCache.Count; using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var item = new Item() { Tag = 10 }; @@ -123,13 +120,11 @@ public void DelayedQueryWithContainsTest() TestHelper.CollectGarbage(true); Assert.AreEqual(0, OtherService2.InstanceCount); - Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems)); } [Test] public void DelayedQueryWithEqualityTest() { - var cachedItems = Domain.QueryCache.Count; using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var item = new Item() { Tag = 10 }; @@ -139,7 +134,6 @@ public void DelayedQueryWithEqualityTest() TestHelper.CollectGarbage(true); Assert.AreEqual(0, OtherService3.InstanceCount); - Assert.That(Domain.QueryCache.Count, Is.EqualTo(cachedItems + 1)); } private void DelayedQueryWithEquality(Session session) diff --git a/Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs b/Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs deleted file mode 100644 index c7e6e85fc9..0000000000 --- a/Orm/Xtensive.Orm.Tests/Linq/ConditionalQueryCachingWithLocalCollectionsTest.cs +++ /dev/null @@ -1,1648 +0,0 @@ -// Copyright (C) 2022 Xtensive LLC. -// This code is distributed under MIT license terms. -// See the License.txt file in the project root for more information. - -using System; -using System.Linq; -using System.Threading.Tasks; -using NUnit.Framework; -using Xtensive.Orm.Configuration; -using Xtensive.Orm.Tests.Linq.ConditionalQueryCachingWithLocalCollectionsTestModel; - -namespace Xtensive.Orm.Tests.Linq.ConditionalQueryCachingWithLocalCollectionsTestModel -{ - [Serializable] - [HierarchyRoot] - public class AllTypesNeverCreatedEntity : Entity - { - [Field, Key] - public int Id { get; private set; } - - [Field] - public bool FBool { get; set; } - - [Field] - public char FChar { get; set; } - - [Field] - public byte FByte { get; set; } - - [Field] - public sbyte FSByte { get; set; } - - [Field] - public short FShort { get; set; } - - [Field] - public ushort FUShort { get; set; } - - [Field] - public int FInt { get; set; } - - [Field] - public uint FUInt { get; set; } - - [Field] - public long FLong { get; set; } - - [Field] - public ulong FULong { get; set; } - - [Field] - public Guid FGuid { get; set; } - - [Field] - public float FFloat { get; set; } - - [Field] - public double FDouble { get; set; } - - [Field] - public decimal FDecimal { get; set; } - - [Field] - public DateTime FDateTime { get; set; } - - [Field] - public TimeSpan FTimeSpan { get; set; } - - [Field] - public string FString { get; set; } - - [Field] - public string FLongString { get; set; } - - // Nullable fields - - [Field(DefaultValue = true)] - public bool? FNBool { get; set; } - - [Field(DefaultValue = 'x')] - public char? FNChar { get; set; } - - [Field(DefaultValue = byte.MaxValue)] - public byte? FNByte { get; set; } - - [Field(DefaultValue = sbyte.MaxValue)] - public sbyte? FNSByte { get; set; } - - [Field(DefaultValue = short.MaxValue)] - public short? FNShort { get; set; } - - [Field] - public ushort? FNUShort { get; set; } - - [Field] - public int? FNInt { get; set; } - - [Field] - public uint? FNUInt { get; set; } - - [Field] - public long? FNLong { get; set; } - - [Field] // SQLite provides only 8 byte signed integer - public ulong? FNULong { get; set; } - - [Field] - public Guid? FNGuid { get; set; } - - [Field] - public float? FNFloat { get; set; } - - [Field] - public double? FNDouble { get; set; } - - [Field] - public decimal? FNDecimal { get; set; } - - [Field] - public DateTime? FNDateTime { get; set; } - - [Field] - public TimeSpan? FNTimeSpan { get; set; } - - public AllTypesNeverCreatedEntity(Session session) - : base(session) - { - } - } - - [Serializable] - [HierarchyRoot] - public class DateTimeOffsetNeverCreatedEntity : Entity - { - [Field, Key] - public int Id { get; private set; } - - [Field] - public DateTimeOffset FDateTimeOffset { get; set; } - - [Field] - public DateTimeOffset? FNDateTimeOffset { get; set; } - - public DateTimeOffsetNeverCreatedEntity(Session session) - : base(session) - { - } - } - - [Serializable] - [HierarchyRoot] - public class PgSqlTypesNeverCreatedEntity : Entity - { - [Field, Key] - public int Id { get; private set; } - - [Field] - public NpgsqlTypes.NpgsqlPoint FPoint { get; set; } - - [Field] - public NpgsqlTypes.NpgsqlPoint? FNPoint { get; set; } - - public PgSqlTypesNeverCreatedEntity(Session session) - : base(session) - { - } - } - - public struct PropsStruct - { - public int IntField { get; set; } - - public long LongField { get; set; } - } - - public struct PropsWrongStruct - { - public int IntField { get; set; } - - public Tuple TupleField { get; set; } - } - - public struct FieldsStruct - { - public int IntField; - - public long LongField; - } - - public struct FieldsWrongStruct - { - public int IntField; - - public Tuple TupleField; - } -} - -namespace Xtensive.Orm.Tests.Linq -{ - public sealed class ConditionalQueryCachingWithLocalCollectionsTest : AutoBuildTest - { - protected override DomainConfiguration BuildConfiguration() - { - var config = base.BuildConfiguration(); - config.Types.Register(typeof(AllTypesNeverCreatedEntity)); - - if (StorageProviderInfo.Instance.CheckAllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset)) - config.Types.Register(typeof(DateTimeOffsetNeverCreatedEntity)); - if (StorageProviderInfo.Instance.CheckProviderIs(StorageProvider.PostgreSql)) - config.Types.Register(typeof(PgSqlTypesNeverCreatedEntity)); - - config.UpgradeMode = DomainUpgradeMode.Recreate; - return config; - } - - protected override void CheckRequirements() => Require.ProviderIsNot(StorageProvider.Sqlite); - - public static Tuple StaticField; - public Tuple Field; - - public static Tuple StaticProperty { get; set; } - public Tuple Property { get; set; } - - - [Test] - public void FieldTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - QueryWithField(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - [Test] - public async Task FieldTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryWithFieldAsync(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - [Test] - public void StaticFieldTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - QueryWithStaticField(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public async Task StaticFieldTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryWithStaticFieldAsync(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public void PropertyTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - QueryWithProperty(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - [Test] - public async Task PropertyTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryWithPropertyAsync(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - [Test] - public void StaticPropertyTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - QueryWithStaticProperty(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public async Task StaticPropertyTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryWithStaticPropertyAsync(session); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public void BooleanTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, true); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (bool?) true); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task BooleanTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, true); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (bool?) true); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void ByteTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (byte) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (byte?) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task ByteTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (byte) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (byte?) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void SByteTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (sbyte) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (sbyte?) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task SByteTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (sbyte) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (sbyte?) 127); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void Int16Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (short) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (short?) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task Int16TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = Domain.OpenSession(); - await using var tx = session.OpenTransaction(); - await QueryAsync(session, (short) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (short?) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void UInt16Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (ushort) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (ushort?) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task UInt16TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = Domain.OpenSession(); - await using var tx = session.OpenTransaction(); - await QueryAsync(session, (ushort) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (ushort?) 256); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void Int32Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (int?) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task Int32TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (int?) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void UInt32Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (uint) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (uint?) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task UInt32TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (uint) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (uint?) 512); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void Int64Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (long) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (long?) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task Int64TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (long) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (long?) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void UInt64Test() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (ulong) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (ulong?) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task UInt64TestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (ulong) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (ulong?) 1024); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void SingleTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, 1024.1f); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (float?) 1024.1f); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task SingleTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, 1024.1f); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (float?) 1024.1f); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void DoubleTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, 1024.2); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (double?) 1024.2); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task DoubleTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, 1024.2); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (double?) 1024.2); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void DecimalTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (decimal) 1024.3); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (decimal?) 1024.3); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task DecimalTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (decimal) 1024.3); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (decimal?) 1024.3); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void CharTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, 'c'); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (char?) 'c'); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task CharTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, 'c'); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (char?) 'c'); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void StringTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, "string"); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public async Task StringTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = Domain.OpenSession(); - await using var tx = session.OpenTransaction(); - await QueryAsync(session, "string"); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public void DateTimeTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, DateTime.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (DateTime?) DateTime.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task DateTimeAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, DateTime.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (DateTime?) DateTime.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void TimeSpanTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, TimeSpan.Zero); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (TimeSpan?) TimeSpan.Zero); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task TimeSpanTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, TimeSpan.Zero); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (TimeSpan?) TimeSpan.Zero); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void GuidTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, Guid.NewGuid()); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (Guid?) Guid.NewGuid()); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task GuidTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, Guid.NewGuid()); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (Guid?) Guid.NewGuid()); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void DateTimeOffsetTest() - { - Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); - - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, DateTimeOffset.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (DateTimeOffset?) DateTimeOffset.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task DateTimeOffsetTestAsync() - { - Require.AllFeaturesSupported(Providers.ProviderFeatures.DateTimeOffset); - - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, DateTimeOffset.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (DateTimeOffset?) DateTimeOffset.Now); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - - [Test] - public void PgSqlTypesTest() - { - Require.ProviderIs(StorageProvider.PostgreSql); - - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, new NpgsqlTypes.NpgsqlPoint(0, 0)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, (NpgsqlTypes.NpgsqlPoint?) new NpgsqlTypes.NpgsqlPoint(0, 0)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task PgSqlTypesTestAsync() - { - Require.ProviderIs(StorageProvider.PostgreSql); - - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, new NpgsqlTypes.NpgsqlPoint(0, 0)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, (NpgsqlTypes.NpgsqlPoint?) new NpgsqlTypes.NpgsqlPoint(0, 0)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void ValueTupleTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, (1, 1.2f)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public async Task ValueTupleTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, (1, 1.2f)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - } - - [Test] - public void CustomStructTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, new PropsStruct() { IntField = 10, LongField = 100 }); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - Query(session, new FieldsStruct() { IntField = 10, LongField = 100 }); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public async Task CustomStructTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, new PropsStruct() { IntField = 10, LongField = 100 }); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 1)); - - await QueryAsync(session, new FieldsStruct() { IntField = 10, LongField = 100 }); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore + 2)); - } - - [Test] - public void UnsupportedTypeTest() - { - var countBefore = Domain.QueryCache.Count; - - using var session = Domain.OpenSession(); - using var tx = session.OpenTransaction(); - Query(session, new Tuple(1)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - [Test] - public async Task UnsupportedTypeTestAsync() - { - var countBefore = Domain.QueryCache.Count; - - await using var session = await Domain.OpenSessionAsync(); - await using var tx = await session.OpenTransactionAsync(); - await QueryAsync(session, new Tuple(1)); - - Assert.That(Domain.QueryCache.Count, Is.EqualTo(countBefore)); - } - - #region General types - - private void QueryWithField(Session session) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == Field.Item1).ToArray(); - } - - private async Task QueryWithFieldAsync(Session session) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == Field.Item1).ToArray(); - } - - private void QueryWithStaticField(Session session) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == StaticField.Item1).ToArray(); - } - - private async Task QueryWithStaticFieldAsync(Session session) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == StaticField.Item1).ToArray(); - } - - private void QueryWithProperty(Session session) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == Property.Item1).ToArray(); - } - - private async Task QueryWithPropertyAsync(Session session) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == Property.Item1).ToArray(); - } - - private void QueryWithStaticProperty(Session session) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == StaticProperty.Item1).ToArray(); - } - - private async Task QueryWithStaticPropertyAsync(Session session) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == StaticProperty.Item1).ToArray(); - } - - private void Query(Session session, Tuple value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.Item1).ToArray(); - } - - private async Task QueryAsync(Session session, Tuple value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.Item1).ToArray(); - } - - private void Query(Session session, bool value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FBool == value).ToArray(); - } - - private async Task QueryAsync(Session session, bool value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FBool == value).ToArray(); - } - - private void Query(Session session, bool? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNBool == value).ToArray(); - } - - private async Task QueryAsync(Session session, bool? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNBool == value).ToArray(); - } - - private void Query(Session session, byte value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FByte == value).ToArray(); - } - - private async Task QueryAsync(Session session, byte value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FByte == value).ToArray(); - } - - private void Query(Session session, byte? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNByte == value).ToArray(); - } - - private async Task QueryAsync(Session session, byte? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNByte == value).ToArray(); - } - - private void Query(Session session, sbyte value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FSByte == value).ToArray(); - } - - private async Task QueryAsync(Session session, sbyte value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FSByte == value).ToArray(); - } - - private void Query(Session session, sbyte? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNSByte == value).ToArray(); - } - - private async Task QueryAsync(Session session, sbyte? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNSByte == value).ToArray(); - } - - private void Query(Session session, short value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FShort == value).ToArray(); - } - - private async Task QueryAsync(Session session, short value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FShort == value).ToArray(); - } - - private void Query(Session session, short? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNShort == value).ToArray(); - } - - private async Task QueryAsync(Session session, short? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNShort == value).ToArray(); - } - - private void Query(Session session, ushort value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FUShort == value).ToArray(); - } - - private async Task QueryAsync(Session session, ushort value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FUShort == value).ToArray(); - } - - private void Query(Session session, ushort? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNUShort == value).ToArray(); - } - - private async Task QueryAsync(Session session, ushort? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNUShort == value).ToArray(); - } - - private void Query(Session session, int value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value).ToArray(); - } - - private async Task QueryAsync(Session session, int value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value).ToArray(); - } - - private void Query(Session session, int? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNInt == value).ToArray(); - } - - private async Task QueryAsync(Session session, int? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNInt == value).ToArray(); - } - - private void Query(Session session, uint value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FUInt == value).ToArray(); - } - - private async Task QueryAsync(Session session, uint value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FUInt == value).ToArray(); - } - - private void Query(Session session, uint? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNUInt == value).ToArray(); - } - - private async Task QueryAsync(Session session, uint? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNUInt == value).ToArray(); - } - - private void Query(Session session, long value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FLong == value).ToArray(); - } - - private async Task QueryAsync(Session session, long value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FLong == value).ToArray(); - } - - private void Query(Session session, long? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNLong == value).ToArray(); - } - - private async Task QueryAsync(Session session, long? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNLong == value).ToArray(); - } - - private void Query(Session session, ulong value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FULong == value).ToArray(); - } - - private static async Task QueryAsync(Session session, ulong value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FULong == value).ToArray(); - } - - private void Query(Session session, ulong? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNULong == value).ToArray(); - } - - private static async Task QueryAsync(Session session, ulong? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNULong == value).ToArray(); - } - - private void Query(Session session, float value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FFloat == value).ToArray(); - } - - private async Task QueryAsync(Session session, float value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FFloat == value).ToArray(); - } - - private void Query(Session session, float? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNFloat == value).ToArray(); - } - - private async Task QueryAsync(Session session, float? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNFloat == value).ToArray(); - } - - private void Query(Session session, double value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDouble == value).ToArray(); - } - - private async Task QueryAsync(Session session, double value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDouble == value).ToArray(); - } - - private void Query(Session session, double? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDouble == value).ToArray(); - } - - private async Task QueryAsync(Session session, double? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDouble == value).ToArray(); - } - - private void Query(Session session, decimal value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDecimal == value).ToArray(); - } - - private async Task QueryAsync(Session session, decimal value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDecimal == value).ToArray(); - } - - private void Query(Session session, decimal? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDecimal == value).ToArray(); - } - - private async Task QueryAsync(Session session, decimal? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDecimal == value).ToArray(); - } - - private void Query(Session session, char value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FChar == value).ToArray(); - } - - private async Task QueryAsync(Session session, char value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FChar == value).ToArray(); - } - - private void Query(Session session, char? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNChar == value).ToArray(); - } - - private async Task QueryAsync(Session session, char? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNChar == value).ToArray(); - } - - private void Query(Session session, string value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FString == value).ToArray(); - } - - private async Task QueryAsync(Session session, string value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FString == value).ToArray(); - } - - private void Query(Session session, DateTime value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDateTime == value).ToArray(); - } - - private async Task QueryAsync(Session session, DateTime value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDateTime == value).ToArray(); - } - - private void Query(Session session, DateTime? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDateTime == value).ToArray(); - } - - private async Task QueryAsync(Session session, DateTime? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDateTime == value).ToArray(); - } - - private void Query(Session session, TimeSpan value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FTimeSpan == value).ToArray(); - } - - private async Task QueryAsync(Session session, TimeSpan value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FTimeSpan == value).ToArray(); - } - - private void Query(Session session, TimeSpan? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNTimeSpan == value).ToArray(); - } - - private async Task QueryAsync(Session session, TimeSpan? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNTimeSpan == value).ToArray(); - } - - private void Query(Session session, Guid value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FGuid == value).ToArray(); - } - - private async Task QueryAsync(Session session, Guid value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FGuid == value).ToArray(); - } - - private void Query(Session session, Guid? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNGuid == value).ToArray(); - } - - private async Task QueryAsync(Session session, Guid? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNGuid == value).ToArray(); - } - - private void Query(Session session, (int value1, float value2) value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.value1 || e.FFloat == value.value2).ToArray(); - } - - private async Task QueryAsync(Session session, (int value1, float value2) value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.value1 || e.FFloat == value.value2).ToArray(); - } - - private void Query(Session session, PropsStruct value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); - } - - private async Task QueryAsync(Session session, PropsStruct value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); - } - - private void Query(Session session, FieldsStruct value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); - } - - private async Task QueryAsync(Session session, FieldsStruct value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FInt == value.IntField || e.FLong == value.LongField).ToArray(); - } - - #endregion - - #region Provider-specific types - - private void Query(Session session, DateTimeOffset value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDateTimeOffset == value).ToArray(); - } - - private async Task QueryAsync(Session session, DateTimeOffset value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FDateTimeOffset == value).ToArray(); - } - - private void Query(Session session, DateTimeOffset? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDateTimeOffset == value).ToArray(); - } - - private async Task QueryAsync(Session session, DateTimeOffset? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNDateTimeOffset == value).ToArray(); - } - - private void Query(Session session, NpgsqlTypes.NpgsqlPoint value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FPoint == value).ToArray(); - } - - private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FPoint == value).ToArray(); - } - - private void Query(Session session, NpgsqlTypes.NpgsqlPoint? value) - { - var ids = new[] { 1, 2 }; - var items = session.Query.Execute(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNPoint == value).ToArray(); - } - - private async Task QueryAsync(Session session, NpgsqlTypes.NpgsqlPoint? value) - { - var ids = new[] { 1, 2 }; - var items = await session.Query.ExecuteAsync(q => q.All().Where(e => e.Id.In(ids))); - _ = items.Where(e => e.FNPoint == value).ToArray(); - } - - #endregion - } -} diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs index a7d53f7a90..684a473415 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryProcessingScope.cs @@ -19,7 +19,6 @@ internal sealed class CompiledQueryProcessingScope public ExtendedExpressionReplacer QueryParameterReplacer { get; } public ParameterContext ParameterContext { get; } public bool Execute { get; } - public bool CheckIfCacheble { get; set; } = false; [field: ThreadStatic] internal static CompiledQueryProcessingScope Current { get; private set; } diff --git a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs index 98f8d95cac..cb4c2497c8 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs @@ -27,11 +27,9 @@ internal class CompiledQueryRunner private readonly object queryKey; private readonly object queryTarget; private readonly ParameterContext outerContext; - private readonly IReadOnlySet supportedTypes; private Parameter queryParameter; private ExtendedExpressionReplacer queryParameterReplacer; - private Type queryTargetType; public QueryResult ExecuteCompiled(Func> query) { @@ -128,7 +126,7 @@ private ParameterizedQuery GetScalarQuery( throw new NotSupportedException(Strings.ExNonLinqCallsAreNotSupportedWithinQueryExecuteDelayed); } - PutQueryToCacheIfAllowed(parameterizedQuery, scope.CheckIfCacheble); + PutQueryToCache(parameterizedQuery); return parameterizedQuery; } @@ -149,7 +147,7 @@ private ParameterizedQuery GetSequenceQuery( parameterizedQuery = (ParameterizedQuery) translatedQuery; } - PutQueryToCacheIfAllowed(parameterizedQuery, scope.CheckIfCacheble); + PutQueryToCache(parameterizedQuery); return parameterizedQuery; } @@ -162,7 +160,7 @@ private void AllocateParameterAndReplacer() return; } - var closureType = queryTargetType = queryTarget.GetType(); + var closureType = queryTarget.GetType(); var parameterType = WellKnownOrmTypes.ParameterOfT.CachedMakeGenericType(closureType); var valueMemberInfo = parameterType.GetProperty(nameof(Parameter.Value), closureType); queryParameter = (Parameter) System.Activator.CreateInstance(parameterType, "pClosure"); @@ -201,81 +199,11 @@ private void AllocateParameterAndReplacer() }); } - private bool IsQueryCacheable() - { - if (queryTargetType==null) { - return true; - } - - if (!queryTargetType.IsClosure()) { - return true; - } - - foreach (var field in queryTargetType.GetFields()) { - if (!IsTypeCacheable(field.FieldType, supportedTypes)) { - return false; - } - } - return true; - } - - private static bool IsTypeCacheable(Type type, IReadOnlySet supportedTypes) - { - var type1 = type.StripNullable(); - var typeInfo = type1.GetTypeInfo(); - if (typeInfo.IsGenericType) { - // IReadOnlyList implementations + ValueTuple<> with different number of type arguments - if (type1.IsValueTuple()) { - foreach (var arg in typeInfo.GenericTypeArguments) { - if (!IsTypeCacheable(arg, supportedTypes)) { - return false; - } - } - return true; - } - var genericDef = typeInfo.GetGenericTypeDefinition(); - return genericDef.IsAssignableTo(WellKnownTypes.IReadOnlyListOfT) && IsTypeCacheable(typeInfo.GetGenericArguments()[0], supportedTypes); - } - else if (typeInfo.IsArray) { - return IsTypeCacheable(type1.GetElementType(), supportedTypes); - } - else { - // enums are handled by their base type so no need to check them - return Type.GetTypeCode(type1) switch { - TypeCode.Boolean => true, - TypeCode.Byte => true, - TypeCode.SByte => true, - TypeCode.Int16 => true, - TypeCode.UInt16 => true, - TypeCode.Int32 => true, - TypeCode.UInt32 => true, - TypeCode.Int64 => true, - TypeCode.UInt64 => true, - TypeCode.Single => true, - TypeCode.Double => true, - TypeCode.Decimal => true, - TypeCode.Char => true, - TypeCode.String => true, - TypeCode.DateTime => true, - TypeCode.Object => typeInfo.IsValueType, - _ => false - }; - } - } - private ParameterizedQuery GetCachedQuery() => domain.QueryCache.TryGetItem(queryKey, true, out var item) ? item.Second : null; - private void PutQueryToCacheIfAllowed(ParameterizedQuery parameterizedQuery, in bool checkIfCacheable) { - if (checkIfCacheable && !IsQueryCacheable()) { - // no .resx used because it is hot path. - if (OrmLog.IsLogged(Logging.LogLevel.Info)) - OrmLog.Info("Query can't be cached because closure type it has references to captures reference" + - " type instances. This will lead to long-living objects in memory."); - return; - } + private void PutQueryToCache(ParameterizedQuery parameterizedQuery) => domain.QueryCache.Add(new Pair(queryKey, parameterizedQuery)); - } private ParameterContext CreateParameterContext(ParameterizedQuery query) { @@ -296,7 +224,6 @@ public CompiledQueryRunner(QueryEndpoint endpoint, object queryKey, object query this.queryKey = new Pair(queryKey, session.StorageNodeId); this.queryTarget = queryTarget; this.outerContext = outerContext; - supportedTypes = domain.StorageProviderInfo.SupportedTypes; } } } 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 1895e243e6..dbe41009a7 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs @@ -1696,7 +1696,6 @@ private ProjectionExpression VisitLocalCollectionSequence(Expression sequ if (compiledQueryScope != null) { var replacer = compiledQueryScope.QueryParameterReplacer; var replace = replacer.Replace(sequence); - compiledQueryScope.CheckIfCacheble = true; var parameter = ParameterAccessorFactory.CreateAccessorExpression>(replace); collectionGetter = parameter.CachingCompile(); } 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); }