diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e0573d7..e7c99e8 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -10,9 +10,11 @@ jobs: uses: actions/checkout@v1 - name: Setup .NET SDK - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 7.0.x + dotnet-version: | + 8.0.x + 10.0.x - name: Install dependencies run: dotnet restore @@ -21,4 +23,4 @@ jobs: run: dotnet build ./Mindbox.Data.Linq.sln --configuration Release --no-restore - name: Test - run: dotnet test --no-restore + run: dotnet test --no-restore diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..d1c34ed --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,9 @@ + + + 12 + Mindbox + Mindbox + Copyright 2019 (c) Mindbox + MIT + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..61397e9 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,17 @@ + + + true + $(NoWarn);NU1507 + + + + + + + + + + + + + diff --git a/Mindbox.Data.Linq.Tests/Mindbox.Data.Linq.Tests.csproj b/Mindbox.Data.Linq.Tests/Mindbox.Data.Linq.Tests.csproj index 940b847..1a8bf8e 100644 --- a/Mindbox.Data.Linq.Tests/Mindbox.Data.Linq.Tests.csproj +++ b/Mindbox.Data.Linq.Tests/Mindbox.Data.Linq.Tests.csproj @@ -1,23 +1,21 @@ - + + net8.0;net10.0 Mindbox.Data.Linq.Tests Mindbox.Data.Linq.Tests - net7.0 false false - mindbox.ru - Itc.Nexus - ru - false + true true true + CS0169 - - - - + + + + - \ No newline at end of file + diff --git a/Mindbox.Data.Linq.Tests/SqlGeneration/ArrayContainsTranslationTests.cs b/Mindbox.Data.Linq.Tests/SqlGeneration/ArrayContainsTranslationTests.cs new file mode 100644 index 0000000..fdee899 --- /dev/null +++ b/Mindbox.Data.Linq.Tests/SqlGeneration/ArrayContainsTranslationTests.cs @@ -0,0 +1,54 @@ +using System; +using System.Data.Linq; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Mindbox.Data.Linq.Tests +{ + /// + /// Tests that array.Contains(value) in LINQ queries is correctly translated to SQL IN clause. + /// In .NET 10, the C# compiler generates MemoryExtensions.Contains(ReadOnlySpan, value) + /// for this pattern instead of Enumerable.Contains — this must produce the same SQL. + /// + [TestClass] + public class ArrayContainsTranslationTests + { + [TestMethod] + public void ArrayContains_TranslatesToSqlIn() + { + var ids = new[] { 1, 2, 3 }; + + using var connection = new DbConnectionStub(); + using var context = new DataContext(connection); + + var query = context.GetTable().Where(t => ids.Contains(t.Id)); + + using var command = context.GetCommand(query); + + Assert.AreEqual( + "SELECT [t0].[Id], [t0].[Discriminator], [t0].[X]" + Environment.NewLine + + "FROM [SimpleTable] AS [t0]" + Environment.NewLine + + "WHERE [t0].[Id] IN (@p0, @p1, @p2)", + command.CommandText); + } + + [TestMethod] + public void ArrayContains_EmptyArray_TranslatesToFalse() + { + var ids = Array.Empty(); + + using var connection = new DbConnectionStub(); + using var context = new DataContext(connection); + + var query = context.GetTable().Where(t => ids.Contains(t.Id)); + + using var command = context.GetCommand(query); + + Assert.AreEqual( + "SELECT [t0].[Id], [t0].[Discriminator], [t0].[X]" + Environment.NewLine + + "FROM [SimpleTable] AS [t0]" + Environment.NewLine + + "WHERE 0 = 1", + command.CommandText); + } + } +} diff --git a/Mindbox.Data.Linq/Mindbox.Data.Linq.csproj b/Mindbox.Data.Linq/Mindbox.Data.Linq.csproj index a773987..61f7142 100644 --- a/Mindbox.Data.Linq/Mindbox.Data.Linq.csproj +++ b/Mindbox.Data.Linq/Mindbox.Data.Linq.csproj @@ -1,30 +1,22 @@ - + - netstandard2.0 - Mindbox.Data.Linq - Mindbox + net8.0 + Mindbox.Data.Linq 3.2.0 A clone of Microsoft System.Data.Linq to allow multi-DLL extensibility and EF compatibility. false - Copyright 2019 (c) Mindbox - Mindbox - latest true true snupkg - 10.7.2$(VersionTag) + 10.8.0$(VersionTag) + SYSLIB0003;SYSLIB0011 - - - - - - - - - - + + + + + diff --git a/Mindbox.Data.Linq/SqlClient/Query/QueryConverter.cs b/Mindbox.Data.Linq/SqlClient/Query/QueryConverter.cs index bf2f79d..4323504 100644 --- a/Mindbox.Data.Linq/SqlClient/Query/QueryConverter.cs +++ b/Mindbox.Data.Linq/SqlClient/Query/QueryConverter.cs @@ -1879,6 +1879,17 @@ private SqlNode VisitMethodCall(MethodCallExpression mc) { if (this.IsSequenceOperatorCall(mc)) { return this.VisitSequenceOperatorCall(mc); } + // In .NET 10, array.Contains(value) in expression trees compiles to + // MemoryExtensions.Contains(ReadOnlySpan.op_Implicit(array), value). + // Translate the same as Enumerable.Contains(array, value). + else if (mc.Method.DeclaringType == typeof(MemoryExtensions) + && mc.Method.Name == "Contains" + && mc.Arguments[0] is MethodCallExpression spanConversion + && spanConversion.Method.ReturnType is { IsGenericType: true } returnType + && returnType.GetGenericTypeDefinition() == typeof(ReadOnlySpan<>) + && spanConversion.Arguments.Count == 1) { + return this.VisitContains(spanConversion.Arguments[0], mc.Arguments[1]); + } else if (IsDataManipulationCall(mc)) { return this.VisitDataManipulationCall(mc); } diff --git a/README.md b/README.md new file mode 100644 index 0000000..e855fb2 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Mindbox.Data.Linq + +A fork of Microsoft's `System.Data.Linq` (LINQ to SQL) that enables multi-assembly extensibility and compatibility with modern .NET. + +## Why this fork? + +The original `System.Data.Linq` ships as a single sealed assembly. This fork splits the internals across multiple DLLs, making it possible to extend the query pipeline — custom SQL translators, mapping providers, and diagnostics hooks — without reflection hacks. + +## Target framework + +`net8.0` + +## Installation + +``` +dotnet add package Mindbox.Data.Linq +``` + +## Usage + +Drop-in replacement for `System.Data.Linq`. Replace the namespace import and use `DataContext` as usual: + +```csharp +using System.Data.Linq; + +using var context = new DataContext(connectionString, mappingSource); +var results = context.GetTable() + .Where(o => o.CustomerId == customerId) + .ToList(); +``` + +## Key differences from System.Data.Linq + +| Feature | System.Data.Linq | Mindbox.Data.Linq | +|---|---|---| +| Multi-assembly extensibility | ✗ | ✓ | +| .NET 10 `array.Contains()` in queries | ✗ | ✓ | +| Target framework | netstandard2.0 | net8.0 | + +## Building + +```bash +dotnet restore +dotnet build --configuration Release +dotnet test +``` + +## License + +MIT — see [LICENSE.txt](LICENSE.txt).