├── .editorconfig
├── .gitignore
├── Build
├── SetVersion.ps1
└── linq2db.snk
├── Directory.Build.props
├── Directory.Packages.props
├── MIT-LICENSE.txt
├── NuGet.config
├── NuGet
├── BuildNuspecs.ps1
├── icon.png
└── linq2db.EntityFrameworkCore.nuspec
├── README.md
├── Source
├── .editorconfig
└── LinqToDB.EntityFrameworkCore
│ ├── EFConnectionInfo.cs
│ ├── EFCoreMetadataReader.cs
│ ├── EFProviderInfo.cs
│ ├── ILinqToDBForEFTools.cs
│ ├── Internal
│ ├── EFCoreExpressionAttribute.cs
│ ├── LinqToDBForEFQueryProvider.cs
│ └── LinqToDBOptionsExtension.cs
│ ├── LinqToDBContextOptionsBuilder.cs
│ ├── LinqToDBExtensionsAdapter.cs
│ ├── LinqToDBForEFExtensions.Async.EF.cs
│ ├── LinqToDBForEFExtensions.Async.cs
│ ├── LinqToDBForEFTools.ContextExtensions.cs
│ ├── LinqToDBForEFTools.ContextOptionsBuilderExtensions.cs
│ ├── LinqToDBForEFTools.Extensions.cs
│ ├── LinqToDBForEFTools.Mapping.cs
│ ├── LinqToDBForEFTools.cs
│ ├── LinqToDBForEFToolsDataConnection.cs
│ ├── LinqToDBForEFToolsDataContext.cs
│ ├── LinqToDBForEFToolsException.cs
│ ├── LinqToDBForEFToolsImplDefault.cs
│ ├── LinqToDBProviderInfo.cs
│ ├── Properties
│ ├── AssemblyInfo.cs
│ └── JetBrains.Annotations.cs
│ └── linq2db.EntityFrameworkCore.csproj
├── Tests
├── .editorconfig
├── Directory.Build.props
├── LinqToDB.EntityFrameworkCore.BaseTests
│ ├── ForMappingTestsBase.cs
│ ├── Interceptors
│ │ ├── Extensions
│ │ │ └── LinqToDBContextOptionsBuilderExtensions.cs
│ │ ├── TestCommandInterceptor.cs
│ │ ├── TestConnectionInterceptor.cs
│ │ ├── TestDataContextInterceptor.cs
│ │ ├── TestEfCoreAndLinqToDBComboInterceptor.cs
│ │ ├── TestEntityServiceInterceptor.cs
│ │ └── TestInterceptor.cs
│ ├── LinqToDB.EntityFrameworkCore.BaseTests.csproj
│ ├── Logging
│ │ ├── LogMessageEntry.cs
│ │ ├── NullExternalScopeProvider.cs
│ │ ├── NullScope.cs
│ │ ├── TestLogger.cs
│ │ ├── TestLoggerExtensions.cs
│ │ └── TestLoggerProvider.cs
│ ├── Models
│ │ ├── ForMapping
│ │ │ ├── ForMappingContextBase.cs
│ │ │ ├── NoIdentity.cs
│ │ │ ├── StringTypes.cs
│ │ │ ├── TypesTable.cs
│ │ │ ├── UIntTable.cs
│ │ │ ├── WithDuplicateProperties.cs
│ │ │ ├── WithIdentity.cs
│ │ │ └── WithInheritance.cs
│ │ └── Northwind
│ │ │ ├── BaseEntity.cs
│ │ │ ├── Category.cs
│ │ │ ├── Customer.cs
│ │ │ ├── CustomerCustomerDemo.cs
│ │ │ ├── CustomerDemographics.cs
│ │ │ ├── CustomerOrderHistory.cs
│ │ │ ├── CustomerQuery.cs
│ │ │ ├── CustomerView.cs
│ │ │ ├── Employee.cs
│ │ │ ├── EmployeeTerritory.cs
│ │ │ ├── NorthwindData.Objects.cs
│ │ │ ├── NorthwindData.cs
│ │ │ ├── Order.cs
│ │ │ ├── OrderDetail.cs
│ │ │ ├── Product.cs
│ │ │ ├── Region.cs
│ │ │ ├── Shipper.cs
│ │ │ ├── Supplier.cs
│ │ │ └── Territory.cs
│ ├── TestUtils.cs
│ └── TestsBase.cs
├── LinqToDB.EntityFrameworkCore.FSharpTests
│ ├── LinqToDB.EntityFrameworkCore.FSharpTests.fsproj
│ └── Tests.fs
├── LinqToDB.EntityFrameworkCore.PomeloMySql.Tests
│ ├── ForMappingTests.cs
│ ├── LinqToDB.EntityFrameworkCore.PomeloMySql.Tests.csproj
│ ├── Models
│ │ ├── ForMapping
│ │ │ └── ForMappingContext.cs
│ │ ├── Northwind.Mapping
│ │ │ ├── CategoriesMap.cs
│ │ │ ├── CustomerCustomerDemoMap.cs
│ │ │ ├── CustomerDemographicsMap.cs
│ │ │ ├── CustomersMap.cs
│ │ │ ├── EmployeeTerritoriesMap.cs
│ │ │ ├── EmployeesMap.cs
│ │ │ ├── OrderDetailsMap.cs
│ │ │ ├── OrderMap.cs
│ │ │ ├── ProductsMap.cs
│ │ │ ├── RegionMap.cs
│ │ │ ├── ShippersMap.cs
│ │ │ ├── SuppliersMap.cs
│ │ │ └── TerritoriesMap.cs
│ │ └── Northwind
│ │ │ └── NorthwindContext.cs
│ └── PomeloMySqlTests.cs
├── LinqToDB.EntityFrameworkCore.PostgreSQL.Tests
│ ├── ForMappingTests.cs
│ ├── LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.csproj
│ ├── Models
│ │ ├── ForMapping
│ │ │ └── ForMappingContext.cs
│ │ └── NpgSqlEntities
│ │ │ ├── EntityWithArrays.cs
│ │ │ ├── EntityWithXmin.cs
│ │ │ ├── Event.cs
│ │ │ ├── EventView.cs
│ │ │ ├── NpgSqlEntitiesContext.cs
│ │ │ └── TimeStampEntity.cs
│ ├── NpgSqlTests.cs
│ └── SampleTests
│ │ ├── AAA.cs
│ │ ├── Child.cs
│ │ ├── DataContextExtensions.cs
│ │ ├── Detail.cs
│ │ ├── Entity.cs
│ │ ├── Entity2Item.cs
│ │ ├── IHasId.cs
│ │ ├── Id.cs
│ │ ├── IdTests.cs
│ │ ├── IdValueConverter.cs
│ │ ├── Item.cs
│ │ ├── ModelBuilderExtensions.cs
│ │ ├── QueryableExtensions.cs
│ │ ├── StringExtensions.cs
│ │ ├── SubDetail.cs
│ │ └── TypeExtensions.cs
├── LinqToDB.EntityFrameworkCore.SQLite.Tests
│ ├── ForMappingTests.cs
│ ├── InterceptorTests.cs
│ ├── LinqToDB.EntityFrameworkCore.SQLite.Tests.csproj
│ ├── Models
│ │ ├── ForMapping
│ │ │ └── ForMappingContext.cs
│ │ ├── Northwind.Mapping
│ │ │ ├── CategoriesMap.cs
│ │ │ ├── CustomerCustomerDemoMap.cs
│ │ │ ├── CustomerDemographicsMap.cs
│ │ │ ├── CustomersMap.cs
│ │ │ ├── EmployeeTerritoriesMap.cs
│ │ │ ├── EmployeesMap.cs
│ │ │ ├── OrderDetailsMap.cs
│ │ │ ├── OrderMap.cs
│ │ │ ├── ProductsMap.cs
│ │ │ ├── RegionMap.cs
│ │ │ ├── ShippersMap.cs
│ │ │ ├── SuppliersMap.cs
│ │ │ └── TerritoriesMap.cs
│ │ └── Northwind
│ │ │ └── NorthwindContext.cs
│ └── SQLiteTests.cs
└── LinqToDB.EntityFrameworkCore.SqlServer.Tests
│ ├── ForMappingTests.cs
│ ├── IssueTests.cs
│ ├── JsonConvertTests.cs
│ ├── LinqToDB.EntityFrameworkCore.SqlServer.Tests.csproj
│ ├── Models
│ ├── ForMapping
│ │ └── ForMappingContext.cs
│ ├── Inheritance
│ │ └── InheritanceContext.cs
│ ├── IssueModel
│ │ ├── Issue117Entities.cs
│ │ ├── Issue73Entity.cs
│ │ └── IssueContext.cs
│ ├── Northwind.Mapping
│ │ ├── BaseEntityMap.cs
│ │ ├── CategoriesMap.cs
│ │ ├── CustomerCustomerDemoMap.cs
│ │ ├── CustomerDemographicsMap.cs
│ │ ├── CustomersMap.cs
│ │ ├── EmployeeTerritoriesMap.cs
│ │ ├── EmployeesMap.cs
│ │ ├── OrderDetailsMap.cs
│ │ ├── OrderMap.cs
│ │ ├── ProductsMap.cs
│ │ ├── RegionMap.cs
│ │ ├── ShippersMap.cs
│ │ ├── SuppliersMap.cs
│ │ └── TerritoriesMap.cs
│ └── Northwind
│ │ └── NorthwindContext.cs
│ ├── Settings.cs
│ ├── ToolsTests.cs
│ └── ValueConversion
│ ├── ConvertorTests.cs
│ ├── IEntity`1.cs
│ ├── IdValueConverterSelector.cs
│ ├── IdValueConverter`2.cs
│ ├── Id`2.cs
│ └── SubDivision.cs
├── azure-pipelines.yml
├── linq2db.EFCore.sln
├── linq2db.EFCore.sln.DotSettings
└── spellcheck.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | #Ignore files build by IDE
2 | *.user
3 | bin/
4 | obj/
5 | _ReSharper*/
6 | /.vs/*
7 | /.tools/
8 | /.idea/
9 |
--------------------------------------------------------------------------------
/Build/SetVersion.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$true)][string]$path,
3 | [Parameter(Mandatory=$true)][string]$version
4 | )
5 |
6 | $ErrorActionPreference = "Stop"
7 | Set-StrictMode -Version Latest
8 |
9 | if ($version) {
10 |
11 | $xmlPath = Resolve-Path "$path"
12 |
13 | $xml = [XML](Get-Content "$xmlPath")
14 | $xml.PreserveWhitespace = $true
15 | $save = $false
16 |
17 | $xPath = "//PropertyGroup/Version"
18 | $nodes = $xml.SelectNodes($xPath)
19 | foreach($node in $nodes) {
20 | $node.InnerXml = $version
21 | $save = $true
22 | }
23 |
24 | if ($save) {
25 | Write-Host "Patched $xmlPath"
26 | $xml.Save($xmlPath)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Build/linq2db.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linq2db/linq2db.EntityFrameworkCore/ad20a61cc1b82c9f861fa71590f4db423bf9c24d/Build/linq2db.snk
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 9.0.0
4 |
5 | Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin
6 | Linq to DB
7 | linq2db.net
8 | 2002-2024 linq2db.net
9 | https://github.com/linq2db/linq2db.EntityFrameworkCore
10 | git
11 |
12 | latest
13 | enable
14 | 9999
15 | prompt
16 | strict
17 | True
18 |
19 | false
20 | false
21 | True
22 | ..\..\Build\linq2db.snk
23 | False
24 |
25 | true
26 | true
27 | true
28 | true
29 | true
30 | true
31 | false
32 | true
33 |
34 | true
35 |
36 |
37 | true
38 |
39 | true
40 |
41 | net8.0
42 |
43 |
44 |
45 | true
46 | true
47 | preview-All
48 | true
49 | true
50 | true
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/NuGet/BuildNuspecs.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$true)][string]$path,
3 | [Parameter(Mandatory=$true)][string]$version,
4 | [Parameter(Mandatory=$false)][string]$branch
5 | )
6 |
7 | $ErrorActionPreference = "Stop"
8 | Set-StrictMode -Version Latest
9 |
10 | if ($version) {
11 |
12 | $nsUri = 'http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd'
13 | $ns = @{ns=$nsUri}
14 | $commit = (git rev-parse HEAD)
15 | if (-not $branch) {
16 | $branch = (git rev-parse --abbrev-ref HEAD)
17 | }
18 |
19 | Get-ChildItem $path | ForEach {
20 | $xmlPath = Resolve-Path $_.FullName
21 |
22 | $xml = [xml] (Get-Content "$xmlPath")
23 | $xml.PreserveWhitespace = $true
24 |
25 | # set version metadata
26 | $child = $xml.CreateElement('version', $nsUri)
27 | $child.InnerText = $version
28 | $xml.package.metadata.AppendChild($child)
29 |
30 | # set repository/commit link
31 | $child = $xml.CreateElement('repository', $nsUri)
32 | $attr = $xml.CreateAttribute('type')
33 | $attr.Value = 'git'
34 | $child.Attributes.Append($attr)
35 | $attr = $xml.CreateAttribute('url')
36 | $attr.Value = 'https://github.com/linq2db/linq2db.EntityFrameworkCore.git'
37 | $child.Attributes.Append($attr)
38 | $attr = $xml.CreateAttribute('branch')
39 | $attr.Value = $branch
40 | $child.Attributes.Append($attr)
41 | $attr = $xml.CreateAttribute('commit')
42 | $attr.Value = $commit
43 | $child.Attributes.Append($attr)
44 | $xml.package.metadata.AppendChild($child)
45 |
46 | $xml.Save($xmlPath)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/NuGet/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linq2db/linq2db.EntityFrameworkCore/ad20a61cc1b82c9f861fa71590f4db423bf9c24d/NuGet/icon.png
--------------------------------------------------------------------------------
/NuGet/linq2db.EntityFrameworkCore.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | linq2db.EntityFrameworkCore
5 | Linq to DB (linq2db) extensions for Entity Framework Core
6 | Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko
7 | Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko
8 | Copyright © 2020-2024 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko
9 | Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext.
10 |
11 | linq linq2db LinqToDB ORM database entity-framework-core EntityFrameworkCore EFCore DB SQL SqlServer SqlCe SqlServerCe MySql Firebird SQLite Oracle ODP PostgreSQL DB2
12 | false
13 | images\icon.png
14 | https://github.com/linq2db/linq2db.EntityFrameworkCore
15 | MIT-LICENSE.txt
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Source/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_diagnostic.CA1827.severity = error # CA1827: Do not use Count/LongCount when Any can be used
3 | dotnet_diagnostic.CA2007.severity = error # CA2007: Do not directly await a Task
4 | dotnet_diagnostic.CA1829.severity = error # CA1829: Use Length/Count property instead of Enumerable.Count method
5 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/EFConnectionInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 |
3 | namespace LinqToDB.EntityFrameworkCore
4 | {
5 | ///
6 | /// Contains database connectivity information, extracted from EF.Core.
7 | ///
8 | public sealed class EFConnectionInfo
9 | {
10 | ///
11 | /// Gets or sets database connection instance.
12 | ///
13 | public DbConnection? Connection { get; set; }
14 |
15 | ///
16 | /// Gets or sets database connection string.
17 | ///
18 | public string? ConnectionString { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/EFProviderInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 |
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 |
6 | namespace LinqToDB.EntityFrameworkCore
7 | {
8 | ///
9 | /// Required integration information about underlying database provider, extracted from EF.Core.
10 | ///
11 | public sealed class EFProviderInfo
12 | {
13 | ///
14 | /// Gets or sets database connection instance.
15 | ///
16 | public DbConnection? Connection { get; set; }
17 |
18 | ///
19 | /// Gets or sets EF.Core context instance.
20 | ///
21 | public DbContext? Context { get; set; }
22 |
23 | ///
24 | /// Gets or sets EF.Core context options instance.
25 | ///
26 | public IDbContextOptions? Options { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using LinqToDB.Mapping;
7 | using LinqToDB.SqlQuery;
8 |
9 | namespace LinqToDB.EntityFrameworkCore.Internal
10 | {
11 | ///
12 | /// Maps Linq To DB expression.
13 | ///
14 | public sealed class EFCoreExpressionAttribute : Sql.ExpressionAttribute
15 | {
16 | ///
17 | /// Creates instance of expression mapper.
18 | ///
19 | /// Mapped expression.
20 | public EFCoreExpressionAttribute(string expression) : base(expression)
21 | {
22 | }
23 |
24 | ///
25 | public override ISqlExpression? GetExpression(
26 | TContext context,
27 | IDataContext dataContext,
28 | SelectQuery query,
29 | Expression expression,
30 | Func converter)
31 | {
32 | var knownExpressions = new List();
33 | if (expression.NodeType == ExpressionType.Call)
34 | {
35 | var mc = (MethodCallExpression) expression;
36 | if (!mc.Method.IsStatic)
37 | knownExpressions.Add(mc.Object!);
38 | knownExpressions.AddRange(mc.Arguments);
39 | }
40 | else
41 | {
42 | var me = (MemberExpression) expression;
43 | knownExpressions.Add(me.Expression!);
44 | }
45 |
46 | var @params = new List(knownExpressions.Select(_ => (ISqlExpression?) null));
47 |
48 | _ = ResolveExpressionValues((context, @params, knownExpressions, converter), Expression!,
49 | static (ctx, v, d) =>
50 | {
51 | var idx = int.Parse(v, CultureInfo.InvariantCulture);
52 |
53 | if (ctx.@params[idx] == null)
54 | ctx.@params[idx] = ctx.converter(ctx.context, ctx.knownExpressions[idx], null);
55 |
56 | return v;
57 | });
58 |
59 | var parameters = @params.Select(p => p ?? new SqlExpression("!!!")).ToArray();
60 | return new SqlExpression(expression.Type, Expression!, Precedence, parameters);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/Internal/LinqToDBOptionsExtension.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | namespace LinqToDB.EntityFrameworkCore.Internal
8 | {
9 | ///
10 | /// Model containing LinqToDB related context options.
11 | ///
12 | public class LinqToDBOptionsExtension : IDbContextOptionsExtension
13 | {
14 | private DbContextOptionsExtensionInfo? _info;
15 |
16 | ///
17 | /// Context options extension info object.
18 | ///
19 | public DbContextOptionsExtensionInfo Info
20 | => _info ??= new LinqToDBExtensionInfo(this);
21 |
22 | ///
23 | /// List of registered LinqToDB interceptors
24 | ///
25 | public virtual DataOptions Options { get; set; }
26 |
27 | ///
28 | /// .ctor
29 | ///
30 | public LinqToDBOptionsExtension()
31 | {
32 | Options = new();
33 | }
34 |
35 | ///
36 | /// .ctor
37 | ///
38 | ///
39 | protected LinqToDBOptionsExtension(LinqToDBOptionsExtension copyFrom)
40 | {
41 | Options = copyFrom.Options;
42 | }
43 |
44 | /// Adds the services required to make the selected options work. This is used when
45 | /// there is no external System.IServiceProvider and EF is maintaining its own service
46 | /// provider internally. This allows database providers (and other extensions) to
47 | /// register their required services when EF is creating an service provider.
48 | /// The collection to add services to
49 | public void ApplyServices(IServiceCollection services)
50 | {
51 | ;
52 | }
53 |
54 | ///
55 | /// Gives the extension a chance to validate that all options in the extension are
56 | /// valid. Most extensions do not have invalid combinations and so this will be a
57 | /// no-op. If options are invalid, then an exception should be thrown.
58 | ///
59 | ///
60 | public void Validate(IDbContextOptions options)
61 | {
62 | ;
63 | }
64 |
65 | private sealed class LinqToDBExtensionInfo : DbContextOptionsExtensionInfo
66 | {
67 | private string? _logFragment;
68 |
69 | public LinqToDBExtensionInfo(IDbContextOptionsExtension extension)
70 | : base(extension)
71 | {
72 | }
73 |
74 | private new LinqToDBOptionsExtension Extension
75 | => (LinqToDBOptionsExtension)base.Extension;
76 |
77 | public override bool IsDatabaseProvider
78 | => false;
79 |
80 | public override string LogFragment
81 | {
82 | get
83 | {
84 | if (_logFragment == null)
85 | {
86 | string logFragment = string.Empty;
87 |
88 | if (Extension.Options.DataContextOptions.Interceptors?.Any() == true)
89 | {
90 | logFragment += $"Interceptors count: {Extension.Options.DataContextOptions.Interceptors.Count}";
91 | }
92 |
93 | _logFragment = logFragment;
94 | }
95 |
96 | return _logFragment;
97 | }
98 | }
99 |
100 | public override int GetServiceProviderHashCode() => 0;
101 |
102 | public override void PopulateDebugInfo(IDictionary debugInfo)
103 | => debugInfo["LinqToDB"] = "1";
104 |
105 | public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => true;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBContextOptionsBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LinqToDB.EntityFrameworkCore
5 | {
6 | using Interceptors;
7 | using Internal;
8 | using Mapping;
9 |
10 | ///
11 | /// Linq To DB context options builder
12 | ///
13 | public class LinqToDBContextOptionsBuilder
14 | {
15 | private readonly LinqToDBOptionsExtension? _extension;
16 |
17 | ///
18 | /// Db context options.
19 | ///
20 | public DbContextOptions DbContextOptions { get; private set; }
21 |
22 | ///
23 | /// .ctor
24 | ///
25 | ///
26 | public LinqToDBContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
27 | {
28 | _extension = optionsBuilder.Options.FindExtension();
29 | DbContextOptions = optionsBuilder.Options;
30 | }
31 |
32 | ///
33 | /// Registers Linq To DB interceptor.
34 | ///
35 | /// The interceptor instance to register.
36 | ///
37 | public LinqToDBContextOptionsBuilder AddInterceptor(IInterceptor interceptor)
38 | {
39 | if (_extension != null)
40 | _extension.Options = _extension.Options.UseInterceptor(interceptor);
41 |
42 | return this;
43 | }
44 |
45 | ///
46 | /// Registers custom Linq To DB MappingSchema.
47 | ///
48 | /// The interceptor instance to register.
49 | ///
50 | public LinqToDBContextOptionsBuilder AddMappingSchema(MappingSchema mappingSchema)
51 | {
52 | if (_extension != null)
53 | _extension.Options = _extension.Options.UseMappingSchema(mappingSchema);
54 |
55 | return this;
56 | }
57 |
58 | ///
59 | /// Registers custom Linq To DB options.
60 | ///
61 | /// Function to setup custom Linq To DB options.
62 | ///
63 | public LinqToDBContextOptionsBuilder AddCustomOptions(Func optionsSetter)
64 | {
65 | if (_extension != null)
66 | _extension.Options = optionsSetter(_extension.Options);
67 |
68 | return this;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.ContextOptionsBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 |
5 | namespace LinqToDB.EntityFrameworkCore
6 | {
7 | using Internal;
8 |
9 | public static partial class LinqToDBForEFTools
10 | {
11 | ///
12 | /// Registers custom options related to LinqToDB provider.
13 | ///
14 | ///
15 | /// Custom options action.
16 | ///
17 | [Obsolete($"Use {nameof(UseLinqToDB)} overload.")]
18 | public static DbContextOptionsBuilder UseLinqToDb(
19 | this DbContextOptionsBuilder optionsBuilder,
20 | Action? linq2dbOptionsAction = null)
21 | => UseLinqToDB(optionsBuilder, linq2dbOptionsAction);
22 |
23 | ///
24 | /// Registers custom options related to LinqToDB provider.
25 | ///
26 | ///
27 | /// Custom options action.
28 | ///
29 | public static DbContextOptionsBuilder UseLinqToDB(
30 | this DbContextOptionsBuilder optionsBuilder,
31 | Action? linq2dbOptionsAction = null)
32 | {
33 | ((IDbContextOptionsBuilderInfrastructure)optionsBuilder)
34 | .AddOrUpdateExtension(GetOrCreateExtension(optionsBuilder));
35 |
36 | linq2dbOptionsAction?.Invoke(new LinqToDBContextOptionsBuilder(optionsBuilder));
37 |
38 | return optionsBuilder;
39 | }
40 |
41 | private static LinqToDBOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder options)
42 | => options.Options.FindExtension()
43 | ?? new LinqToDBOptionsExtension();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Extensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace LinqToDB.EntityFrameworkCore
4 | {
5 | public static partial class LinqToDBForEFTools
6 | {
7 | ///
8 | /// Converts EF.Core instance to LINQ To DB instance.
9 | ///
10 | /// Mapping entity type.
11 | /// EF.Core instance.
12 | /// LINQ To DB instance.
13 | public static ITable ToLinqToDBTable(this DbSet dbSet)
14 | where T : class
15 | {
16 | var context = Implementation.GetCurrentContext(dbSet)
17 | ?? throw new LinqToDBForEFToolsException($"Can not load current context from {nameof(dbSet)}");
18 | #pragma warning disable CA2000 // Dispose objects before losing scope
19 | var dc = CreateLinqToDBContext(context);
20 | #pragma warning restore CA2000 // Dispose objects before losing scope
21 | return dc.GetTable();
22 | }
23 |
24 | ///
25 | /// Converts EF.Core instance to LINQ To DB instance
26 | /// using existing LINQ To DB instance.
27 | ///
28 | /// Mapping entity type.
29 | /// EF.Core instance.
30 | /// LINQ To DB data context instance.
31 | /// LINQ To DB instance.
32 | public static ITable ToLinqToDBTable(this DbSet dbSet, IDataContext dataContext)
33 | where T : class
34 | {
35 | return dataContext.GetTable();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Mapping.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Linq.Expressions;
4 | using System.Reflection;
5 |
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace LinqToDB.EntityFrameworkCore
9 | {
10 | using Expressions;
11 | using Extensions;
12 |
13 | public partial class LinqToDBForEFTools
14 | {
15 | static void InitializeMapping()
16 | {
17 | Linq.Expressions.MapMember(
18 | (DbFunctions f, string m, string p) => f.Like(m, p), (f, m, p) => Sql.Like(m, p));
19 |
20 | // InitializeSqlServerMapping();
21 | }
22 |
23 |
24 | #region Sql Server
25 |
26 | static Sql.DateParts? GetDatePart(string name)
27 | {
28 | return name switch
29 | {
30 | "Year" => (Sql.DateParts?)Sql.DateParts.Year,
31 | "Day" => (Sql.DateParts?)Sql.DateParts.Day,
32 | "Month" => (Sql.DateParts?)Sql.DateParts.Month,
33 | "Hour" => (Sql.DateParts?)Sql.DateParts.Hour,
34 | "Minute" => (Sql.DateParts?)Sql.DateParts.Minute,
35 | "Second" => (Sql.DateParts?)Sql.DateParts.Second,
36 | "Millisecond" => (Sql.DateParts?)Sql.DateParts.Millisecond,
37 | _ => null,
38 | };
39 | }
40 |
41 | ///
42 | /// Initializes SQL Server's DbFunctions dynamically to avoid dependency
43 | ///
44 | static void InitializeSqlServerMapping()
45 | {
46 | var type = Type.GetType("Microsoft.EntityFrameworkCore.SqlServerDbFunctionsExtensions, Microsoft.EntityFrameworkCore.SqlServer", false);
47 |
48 | if (type == null)
49 | return;
50 |
51 | var sqlServerMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
52 | .Where(m => m.GetParameters().Length > 0)
53 | .ToArray();
54 |
55 | var dbFunctionsParameter = Expression.Parameter(typeof(DateTime), "dbFunctions");
56 |
57 | var dateDiffStr = "DateDiff";
58 | var dateDiffMethods = sqlServerMethods.Where(m => m.Name.StartsWith(dateDiffStr)).ToArray();
59 |
60 | var dateDiffMethod = MemberHelper.MethodOf(() => Sql.DateDiff(Sql.DateParts.Day, (DateTime?)null, null));
61 |
62 | foreach (var method in dateDiffMethods)
63 | {
64 | var datePart = GetDatePart(method.Name.Substring(dateDiffStr.Length));
65 | if (datePart == null)
66 | continue;
67 |
68 | var parameters = method.GetParameters();
69 | if (parameters.Length < 3)
70 | continue;
71 |
72 | var boundaryType = parameters[1].ParameterType;
73 | if (boundaryType.ToUnderlying() != typeof(DateTime))
74 | continue;
75 |
76 | var startParameter = Expression.Parameter(boundaryType, "start");
77 | var endParameter = Expression.Parameter(boundaryType, "end");
78 |
79 | var startExpr = startParameter.Type != typeof(DateTime?)
80 | ? (Expression) Expression.Convert(startParameter, typeof(DateTime?))
81 | : startParameter;
82 |
83 | var endExpr = endParameter.Type != typeof(DateTime?)
84 | ? (Expression) Expression.Convert(endParameter, typeof(DateTime?))
85 | : endParameter;
86 |
87 |
88 | var body = Expression.Call(dateDiffMethod, Expression.Constant(datePart.Value), startExpr, endExpr);
89 | var lambda = Expression.Lambda(body, dbFunctionsParameter, startParameter, endParameter);
90 |
91 | Linq.Expressions.MapMember(method, lambda);
92 | }
93 | }
94 |
95 | #endregion
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 |
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace LinqToDB.EntityFrameworkCore
8 | {
9 |
10 | using DataProvider;
11 | using Linq;
12 |
13 | ///
14 | /// Linq To DB EF.Core data context.
15 | ///
16 | public class LinqToDBForEFToolsDataContext : DataContext, IExpressionPreprocessor
17 | {
18 | readonly DbContext? _context;
19 | readonly IModel _model;
20 | readonly Func? _transformFunc;
21 |
22 | ///
23 | /// Creates instance of context.
24 | ///
25 | /// EF.Core database context.
26 | /// lin2db database provider instance.
27 | /// Connection string.
28 | /// EF.Core model.
29 | /// Expression converter.
30 | public LinqToDBForEFToolsDataContext(
31 | DbContext? context,
32 | IDataProvider dataProvider,
33 | string connectionString,
34 | IModel model,
35 | Func? transformFunc) : base(dataProvider, connectionString)
36 | {
37 | _context = context;
38 | _model = model;
39 | _transformFunc = transformFunc;
40 | }
41 |
42 | ///
43 | /// Converts expression using convert function, passed to context.
44 | ///
45 | /// Expression to convert.
46 | /// Converted expression.
47 | public Expression ProcessExpression(Expression expression)
48 | {
49 | if (_transformFunc == null)
50 | return expression;
51 | return _transformFunc(expression, this, _context, _model);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LinqToDB.EntityFrameworkCore
4 | {
5 | ///
6 | /// Exception class for EF.Core to LINQ To DB integration issues.
7 | ///
8 | public sealed class LinqToDBForEFToolsException : Exception
9 | {
10 | ///
11 | /// Creates new instance of exception.
12 | ///
13 | public LinqToDBForEFToolsException()
14 | {
15 | }
16 |
17 | ///
18 | /// Creates new instance of exception.
19 | ///
20 | /// Exception message.
21 | public LinqToDBForEFToolsException(string message) : base(message)
22 | {
23 | }
24 |
25 | ///
26 | /// Creates new instance of exception when it generated for other exception.
27 | ///
28 | /// Exception message.
29 | /// Original exception.
30 | public LinqToDBForEFToolsException(string message, Exception innerException) : base(message, innerException)
31 | {
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/LinqToDBProviderInfo.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.EntityFrameworkCore
2 | {
3 | ///
4 | /// Stores LINQ To DB database provider information.
5 | ///
6 | public sealed class LinqToDBProviderInfo
7 | {
8 | ///
9 | /// Server version. Currently is not used.
10 | ///
11 | public string? Version { get; set; }
12 |
13 | ///
14 | /// Gets or sets LINQ To DB provider name.
15 | /// for available providers.
16 | ///
17 | public string? ProviderName { get; set; }
18 |
19 | ///
20 | /// Replaces null values in current instance with values from parameter.
21 | ///
22 | /// Provider information to merge into current object.
23 | public void Merge(LinqToDBProviderInfo? providerInfo)
24 | {
25 | if (providerInfo != null)
26 | {
27 | Version ??= providerInfo.Version;
28 | ProviderName ??= providerInfo.ProviderName;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [module: SkipLocalsInit]
4 |
--------------------------------------------------------------------------------
/Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext.
5 | Linq to DB (linq2db) extensions for Entity Framework Core
6 | $(Title)
7 |
8 | LinqToDB.EntityFrameworkCore
9 | bin\$(Configuration)\$(TargetFramework)\linq2db.EntityFrameworkCore.xml
10 |
11 |
12 | EF1001
13 |
14 |
15 |
16 | portable
17 | true
18 | true
19 |
20 |
21 |
22 |
23 |
24 |
25 | all
26 | runtime; build; native; contentfiles; analyzers
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_diagnostic.NUnit4001.severity = none
3 |
--------------------------------------------------------------------------------
/Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | net8.0
6 |
7 | $(NoWarn);CS1591
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Interceptors/Extensions/LinqToDBContextOptionsBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using LinqToDB.Interceptors;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 |
5 | namespace LinqToDB.EntityFrameworkCore.BaseTests.Interceptors.Extensions
6 | {
7 | public static class LinqToDBContextOptionsBuilderExtensions
8 | {
9 | public static void UseEfCoreRegisteredInterceptorsIfPossible(this LinqToDBContextOptionsBuilder builder)
10 | {
11 | var coreEfExtension = builder.DbContextOptions.FindExtension();
12 | if (coreEfExtension?.Interceptors != null)
13 | {
14 | foreach (var comboInterceptor in coreEfExtension.Interceptors.OfType())
15 | {
16 | builder.AddInterceptor(comboInterceptor);
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Interceptors/TestCommandInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Data.Common;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using LinqToDB.Common;
6 | using LinqToDB.Interceptors;
7 |
8 | namespace LinqToDB.EntityFrameworkCore.BaseTests.Interceptors
9 | {
10 | public class TestCommandInterceptor : TestInterceptor, ICommandInterceptor
11 | {
12 | public void AfterExecuteReader(CommandEventData eventData, DbCommand command, CommandBehavior commandBehavior, DbDataReader dataReader)
13 | {
14 | HasInterceptorBeenInvoked = true;
15 | }
16 |
17 | public void BeforeReaderDispose(CommandEventData eventData, DbCommand? command, DbDataReader dataReader)
18 | {
19 | HasInterceptorBeenInvoked = true;
20 | }
21 |
22 | public Task BeforeReaderDisposeAsync(CommandEventData eventData, DbCommand? command, DbDataReader dataReader)
23 | {
24 | HasInterceptorBeenInvoked = true;
25 | return Task.CompletedTask;
26 | }
27 |
28 | public DbCommand CommandInitialized(CommandEventData eventData, DbCommand command)
29 | {
30 | HasInterceptorBeenInvoked = true;
31 | return command;
32 | }
33 |
34 | public Option ExecuteNonQuery(CommandEventData eventData, DbCommand command, Option result)
35 | {
36 | HasInterceptorBeenInvoked = true;
37 | return result;
38 | }
39 |
40 | public Task