├── .gitattributes ├── .gitignore ├── README.md ├── Ralms.EntityFrameworkCore.Extensions.sln ├── Ralms.EntityFrameworkCore.Extensions ├── DateDiff │ ├── EFCore.cs │ ├── EFEnum.cs │ └── EFFunctions.cs ├── Helper │ └── RalmsExtensionSql.cs ├── Ralms.EntityFrameworkCore.Extensions.csproj └── WithHint │ ├── Extensions │ ├── RalmsDbContextOptionsBuilderExtensions.cs │ └── RalmsServiceCollectionExtensions.cs │ ├── Query │ ├── Operators │ │ ├── WithExpressionNode.cs │ │ └── WithResultOperator.cs │ ├── QueryGenerator.cs │ ├── QueryGeneratorFactory.cs │ └── TableExpressionExtension.cs │ ├── RalmsCompilationQueryableFactory.cs │ ├── RalmsEntityQueryableExpressionVisitor.cs │ ├── RalmsEntityQueryableExpressionVisitorFactory.cs │ ├── RalmsExOptionsExtension.cs │ ├── RalmsQueryableExtensions.cs │ └── SqlServerHints.cs ├── Ralms.EntityFrameworkCore.Tests ├── Other │ ├── Model.cs │ └── SampleContext.cs ├── Ralms.EntityFrameworkCore.Tests.csproj ├── TestDataDiff.cs └── TestWithHint.cs └── appveyor.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Ralms.EntityFrameworkCore.Extensions for EntityFramework Core 3 | 4 | ## Supports: 5 | DateDiff & Query Hints (Only SQL Server) 6 | 7 | 8 | ## Example of use DateDiff 9 | 10 | ```csharp 11 | var list = db 12 | .People 13 | .Where(p => EFCore.DateDiff(DatePart.day, DateTime.Now, p.Birthday) < 50) 14 | .ToList(); 15 | ``` 16 | 17 | ## Example of use Ralms.EntityFrameworkCore.Extensions 18 | 19 | 20 | ```csharp 21 | 22 | // Enable Extension 23 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 24 | { 25 | optionsBuilder 26 | .UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=SampleExtension;Integrated Security=True;") 27 | .RalmsExtendFunctions(); 28 | } 29 | 30 | // Sample Use 31 | var list = db 32 | .People 33 | .WithHint(SqlServerHints.NOLOCK) 34 | .Take(1) 35 | .ToList(); 36 | 37 | var list = db 38 | .People 39 | .WithHint(SqlServerHints.UPDLOCK) 40 | .Take(1) 41 | .ToList(); 42 | ``` 43 | ```sql 44 | SELECT [b].[Id], [b].[Date], [b].[Name] 45 | FROM [Blogs] AS [b] WITH (UPDLOCK) 46 | ``` 47 | 48 | 49 | ## Example of use ToSql 50 | * New extension to design the SQL output 51 | 52 | 53 | ```csharp 54 | var sql = _db 55 | .Blogs 56 | .Where(p => EFCore.DateDiff(DatePart.day, DateTime.Now, p.Date) < 50) 57 | .ToSql(); 58 | ``` 59 | 60 | **Output SQL** 61 | 62 | ```SQL 63 | SELECT [p].[Id], [p].[Date], [p].[Name] 64 | FROM [Blogs] AS [p] 65 | WHERE DATEDIFF(day, GETDATE(), [p].[Date]) < 50 66 | ``` 67 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27604.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ralms.EntityFrameworkCore.Extensions", "Ralms.EntityFrameworkCore.Extensions\Ralms.EntityFrameworkCore.Extensions.csproj", "{23B17A02-0168-4DC0-AB2F-2ACE31FF4022}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ralms.EntityFrameworkCore.Tests", "Ralms.EntityFrameworkCore.Tests\Ralms.EntityFrameworkCore.Tests.csproj", "{B5C8486C-B939-4F73-9B52-223B7EF21FA0}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {23B17A02-0168-4DC0-AB2F-2ACE31FF4022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {23B17A02-0168-4DC0-AB2F-2ACE31FF4022}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {23B17A02-0168-4DC0-AB2F-2ACE31FF4022}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {23B17A02-0168-4DC0-AB2F-2ACE31FF4022}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {B5C8486C-B939-4F73-9B52-223B7EF21FA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {B5C8486C-B939-4F73-9B52-223B7EF21FA0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {B5C8486C-B939-4F73-9B52-223B7EF21FA0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {B5C8486C-B939-4F73-9B52-223B7EF21FA0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {83F9CB88-8A30-4280-84A1-366540CBBF2E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/DateDiff/EFCore.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.EntityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System; 18 | using System.Linq; 19 | using System.Linq.Expressions; 20 | using Microsoft.EntityFrameworkCore.Query.Expressions; 21 | 22 | namespace Microsoft.EntityFrameworkCore 23 | { 24 | public static class EFCore 25 | { 26 | public static void EnableSqlServerDateDIFF(this ModelBuilder builder) 27 | { 28 | builder.HasDbFunction(typeof(EFCore) 29 | .GetMethod(nameof(EFCore.DateDiff))) 30 | .HasTranslation(args => 31 | { 32 | var arguments = args.ToList(); 33 | arguments[0] = new SqlFragmentExpression(((ConstantExpression)arguments.First()).Value.ToString()); 34 | return new SqlFunctionExpression( 35 | "DATEDIFF", 36 | typeof(int), 37 | arguments); 38 | }); 39 | } 40 | 41 | public static int? DateDiff(DatePart datePart, object start, object end) 42 | { 43 | var startDate = start.GetType() == typeof(DateTime) 44 | ? (DateTime)start 45 | : (DateTimeOffset)start; 46 | 47 | var endDate = end.GetType() == typeof(DateTime) 48 | ? (DateTime)end 49 | : (DateTimeOffset)end; 50 | 51 | switch (datePart) 52 | { 53 | case DatePart.day: 54 | return EFFunctions.DateDiffDay(startDate, endDate); 55 | case DatePart.month: 56 | return EFFunctions.DateDiffMonth(startDate, endDate); 57 | case DatePart.year: 58 | return EFFunctions.DateDiffYear(startDate, endDate); 59 | case DatePart.hour: 60 | return EFFunctions.DateDiffHour(startDate, endDate); 61 | case DatePart.minute: 62 | return EFFunctions.DateDiffMinute(startDate, endDate); 63 | case DatePart.second: 64 | return EFFunctions.DateDiffSecond(startDate, endDate); 65 | case DatePart.millisecond: 66 | return EFFunctions.DateDiffMillisecond(startDate, endDate); 67 | case DatePart.microsecond: 68 | return EFFunctions.DateDiffMicrosecond(startDate, endDate); 69 | case DatePart.nanosecond: 70 | return EFFunctions.DateDiffNanosecond(startDate, endDate); 71 | default: 72 | throw new Exception("Please enter a valid DATEPART!"); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/DateDiff/EFEnum.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | namespace Microsoft.EntityFrameworkCore 18 | { 19 | public enum DatePart 20 | { 21 | day, 22 | month, 23 | year, 24 | hour, 25 | minute, 26 | second, 27 | millisecond, 28 | microsecond, 29 | nanosecond 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/DateDiff/EFFunctions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System; 18 | 19 | namespace Microsoft.EntityFrameworkCore 20 | { 21 | public static class EFFunctions 22 | { 23 | public static int DateDiffYear( 24 | DateTime startDate, 25 | DateTime endDate) 26 | => endDate.Year - startDate.Year; 27 | 28 | public static int? DateDiffYear( 29 | DateTime? startDate, 30 | DateTime? endDate) 31 | => (startDate.HasValue && endDate.HasValue) 32 | ? (int?)DateDiffYear(startDate.Value, endDate.Value) 33 | : null; 34 | 35 | public static int DateDiffYear( 36 | DateTimeOffset startDate, 37 | DateTimeOffset endDate) 38 | => DateDiffYear(startDate.UtcDateTime, endDate.UtcDateTime); 39 | 40 | public static int? DateDiffYear( 41 | DateTimeOffset? startDate, 42 | DateTimeOffset? endDate) 43 | => (startDate.HasValue && endDate.HasValue) 44 | ? (int?)DateDiffYear(startDate.Value, endDate.Value) 45 | : null; 46 | 47 | public static int DateDiffMonth( 48 | DateTime startDate, 49 | DateTime endDate) 50 | => 12 * (endDate.Year - startDate.Year) + endDate.Month - startDate.Month; 51 | 52 | public static int? DateDiffMonth( 53 | DateTime? startDate, 54 | DateTime? endDate) 55 | => (startDate.HasValue && endDate.HasValue) 56 | ? (int?)DateDiffMonth(startDate.Value, endDate.Value) 57 | : null; 58 | 59 | public static int DateDiffMonth( 60 | DateTimeOffset startDate, 61 | DateTimeOffset endDate) 62 | => DateDiffMonth(startDate.UtcDateTime, endDate.UtcDateTime); 63 | 64 | public static int? DateDiffMonth( 65 | DateTimeOffset? startDate, 66 | DateTimeOffset? endDate) 67 | => (startDate.HasValue && endDate.HasValue) 68 | ? (int?)DateDiffMonth(startDate.Value, endDate.Value) 69 | : null; 70 | 71 | public static int DateDiffDay( 72 | DateTime startDate, 73 | DateTime endDate) 74 | => (endDate.Date - startDate.Date).Days; 75 | 76 | public static int? DateDiffDay( 77 | DateTime? startDate, 78 | DateTime? endDate) 79 | => (startDate.HasValue && endDate.HasValue) 80 | ? (int?)DateDiffDay(startDate.Value, endDate.Value) 81 | : null; 82 | 83 | public static int DateDiffDay( 84 | DateTimeOffset startDate, 85 | DateTimeOffset endDate) 86 | => DateDiffDay(startDate.UtcDateTime, endDate.UtcDateTime); 87 | 88 | public static int? DateDiffDay( 89 | DateTimeOffset? startDate, 90 | DateTimeOffset? endDate) 91 | => (startDate.HasValue && endDate.HasValue) 92 | ? (int?)DateDiffDay(startDate.Value, endDate.Value) 93 | : null; 94 | 95 | public static int DateDiffHour( 96 | DateTime startDate, 97 | DateTime endDate) 98 | { 99 | checked 100 | { 101 | return DateDiffDay(startDate, endDate) * 24 + endDate.Hour - startDate.Hour; 102 | } 103 | } 104 | 105 | public static int? DateDiffHour( 106 | DateTime? startDate, 107 | DateTime? endDate) 108 | => (startDate.HasValue && endDate.HasValue) 109 | ? (int?)DateDiffHour(startDate.Value, endDate.Value) 110 | : null; 111 | 112 | public static int DateDiffHour( 113 | DateTimeOffset startDate, 114 | DateTimeOffset endDate) 115 | => DateDiffHour(startDate.UtcDateTime, endDate.UtcDateTime); 116 | 117 | public static int? DateDiffHour( 118 | DateTimeOffset? startDate, 119 | DateTimeOffset? endDate) 120 | => (startDate.HasValue && endDate.HasValue) 121 | ? (int?)DateDiffHour(startDate.Value, endDate.Value) 122 | : null; 123 | 124 | public static int DateDiffMinute( 125 | DateTime startDate, 126 | DateTime endDate) 127 | { 128 | checked 129 | { 130 | return DateDiffHour(startDate, endDate) * 60 + endDate.Minute - startDate.Minute; 131 | } 132 | } 133 | 134 | public static int? DateDiffMinute( 135 | DateTime? startDate, 136 | DateTime? endDate) 137 | => (startDate.HasValue && endDate.HasValue) 138 | ? (int?)DateDiffMinute(startDate.Value, endDate.Value) 139 | : null; 140 | 141 | public static int DateDiffMinute( 142 | DateTimeOffset startDate, 143 | DateTimeOffset endDate) 144 | => DateDiffMinute(startDate.UtcDateTime, endDate.UtcDateTime); 145 | 146 | public static int? DateDiffMinute( 147 | DateTimeOffset? startDate, 148 | DateTimeOffset? endDate) 149 | => (startDate.HasValue && endDate.HasValue) 150 | ? (int?)DateDiffMinute(startDate.Value, endDate.Value) 151 | : null; 152 | 153 | public static int DateDiffSecond( 154 | DateTime startDate, 155 | DateTime endDate) 156 | { 157 | checked 158 | { 159 | return DateDiffMinute(startDate, endDate) * 60 + endDate.Second - startDate.Second; 160 | } 161 | } 162 | 163 | public static int? DateDiffSecond( 164 | DateTime? startDate, 165 | DateTime? endDate) 166 | => (startDate.HasValue && endDate.HasValue) 167 | ? (int?)DateDiffSecond(startDate.Value, endDate.Value) 168 | : null; 169 | 170 | public static int DateDiffSecond( 171 | DateTimeOffset startDate, 172 | DateTimeOffset endDate) 173 | => DateDiffSecond(startDate.UtcDateTime, endDate.UtcDateTime); 174 | 175 | public static int? DateDiffSecond( 176 | DateTimeOffset? startDate, 177 | DateTimeOffset? endDate) 178 | => (startDate.HasValue && endDate.HasValue) 179 | ? (int?)DateDiffSecond(startDate.Value, endDate.Value) 180 | : null; 181 | 182 | public static int DateDiffMillisecond( 183 | DateTime startDate, 184 | DateTime endDate) 185 | { 186 | checked 187 | { 188 | return DateDiffSecond(startDate, endDate) * 1000 + endDate.Millisecond - startDate.Millisecond; 189 | } 190 | } 191 | 192 | public static int? DateDiffMillisecond( 193 | DateTime? startDate, 194 | DateTime? endDate) 195 | => (startDate.HasValue && endDate.HasValue) 196 | ? (int?)DateDiffMillisecond(startDate.Value, endDate.Value) 197 | : null; 198 | 199 | public static int DateDiffMillisecond( 200 | DateTimeOffset startDate, 201 | DateTimeOffset endDate) 202 | => DateDiffMillisecond(startDate.UtcDateTime, endDate.UtcDateTime); 203 | 204 | public static int? DateDiffMillisecond( 205 | DateTimeOffset? startDate, 206 | DateTimeOffset? endDate) 207 | => (startDate.HasValue && endDate.HasValue) 208 | ? (int?)DateDiffMillisecond(startDate.Value, endDate.Value) 209 | : null; 210 | 211 | public static int DateDiffMicrosecond( 212 | DateTime startDate, 213 | DateTime endDate) 214 | { 215 | checked 216 | { 217 | return (int)((endDate.Ticks - startDate.Ticks) / 10); 218 | } 219 | } 220 | 221 | public static int? DateDiffMicrosecond( 222 | DateTime? startDate, 223 | DateTime? endDate) 224 | => (startDate.HasValue && endDate.HasValue) 225 | ? (int?)DateDiffMicrosecond(startDate.Value, endDate.Value) 226 | : null; 227 | 228 | public static int DateDiffMicrosecond( 229 | DateTimeOffset startDate, 230 | DateTimeOffset endDate) 231 | => DateDiffMicrosecond(startDate.UtcDateTime, endDate.UtcDateTime); 232 | 233 | public static int? DateDiffMicrosecond( 234 | DateTimeOffset? startDate, 235 | DateTimeOffset? endDate) 236 | => (startDate.HasValue && endDate.HasValue) 237 | ? (int?)DateDiffMicrosecond(startDate.Value, endDate.Value) 238 | : null; 239 | 240 | public static int DateDiffNanosecond( 241 | DateTime startDate, 242 | DateTime endDate) 243 | { 244 | checked 245 | { 246 | return (int)((endDate.Ticks - startDate.Ticks) * 100); 247 | } 248 | } 249 | 250 | public static int? DateDiffNanosecond( 251 | DateTime? startDate, 252 | DateTime? endDate) 253 | => (startDate.HasValue && endDate.HasValue) 254 | ? (int?)DateDiffNanosecond(startDate.Value, endDate.Value) 255 | : null; 256 | 257 | public static int DateDiffNanosecond( 258 | DateTimeOffset startDate, 259 | DateTimeOffset endDate) 260 | => DateDiffNanosecond(startDate.UtcDateTime, endDate.UtcDateTime); 261 | 262 | public static int? DateDiffNanosecond( 263 | DateTimeOffset? startDate, 264 | DateTimeOffset? endDate) 265 | => (startDate.HasValue && endDate.HasValue) 266 | ? (int?)DateDiffNanosecond(startDate.Value, endDate.Value) 267 | : null; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/Helper/RalmsExtensionSql.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query; 18 | using Microsoft.EntityFrameworkCore.Query.Internal; 19 | using Microsoft.EntityFrameworkCore.Storage; 20 | using System.Linq; 21 | using System.Reflection; 22 | 23 | namespace Microsoft.EntityFrameworkCore 24 | { 25 | public static class RalmsExtensionSql 26 | { 27 | private static readonly TypeInfo _queryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo(); 28 | 29 | private static readonly FieldInfo _queryCompiler 30 | = typeof(EntityQueryProvider) 31 | .GetTypeInfo() 32 | .DeclaredFields 33 | .Single(x => x.Name == "_queryCompiler"); 34 | 35 | private static readonly FieldInfo _queryModelGenerator 36 | = _queryCompilerTypeInfo 37 | .DeclaredFields 38 | .Single(x => x.Name == "_queryModelGenerator"); 39 | 40 | private static readonly FieldInfo _database = _queryCompilerTypeInfo 41 | .DeclaredFields 42 | .Single(x => x.Name == "_database"); 43 | 44 | private static readonly PropertyInfo _dependencies 45 | = typeof(Database) 46 | .GetTypeInfo() 47 | .DeclaredProperties 48 | .Single(x => x.Name == "Dependencies"); 49 | 50 | public static string ToSql(this IQueryable queryable) 51 | where T : class 52 | { 53 | var queryCompiler = _queryCompiler.GetValue(queryable.Provider) as IQueryCompiler; 54 | var queryModel = (_queryModelGenerator.GetValue(queryCompiler) as IQueryModelGenerator).ParseQuery(queryable.Expression); 55 | var queryCompilationContextFactory 56 | = ((DatabaseDependencies)_dependencies.GetValue(_database.GetValue(queryCompiler))) 57 | .QueryCompilationContextFactory; 58 | 59 | var queryCompilationContext = queryCompilationContextFactory.Create(false); 60 | var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor(); 61 | 62 | modelVisitor.CreateQueryExecutor(queryModel); 63 | 64 | return modelVisitor 65 | .Queries 66 | .FirstOrDefault() 67 | .ToString(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/Ralms.EntityFrameworkCore.Extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | Copyright © 2018 Rafael Almeida 7 | Ralms.EntityFrameworkCore; SQL Server Query Hints; Hints; EntityFrameworkCore; Entity Framework Core;entity-framework-core;EF;Data;O/RM 8 | Extension for EntityFrameworkCore 9 | git 10 | Rafael Almeida 11 | Ralms.net - ralms@ralms.net 12 | 1.0.5 13 | https://github.com/ralmsdeveloper/Ralms.EntityFrameworkCore.Extensions 14 | 1.0.5.0 15 | https://github.com/ralmsdeveloper/Ralms.EntityFrameworkCore.Extensions 16 | 1.0.5.0 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | TextTemplatingFileGenerator 28 | ProxiesStrings.Designer.cs 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | True 39 | True 40 | ProxiesStrings.Designer.tt 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Extensions/RalmsDbContextOptionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Infrastructure; 18 | using Ralms.EntityFrameworkCore.Extensions; 19 | 20 | namespace Microsoft.EntityFrameworkCore 21 | { 22 | public static class RalmsDbContextOptionsBuilderExtensions 23 | { 24 | public static DbContextOptionsBuilder RalmsExtendFunctions(this DbContextOptionsBuilder optionsBuilder) 25 | { 26 | var extension = optionsBuilder.Options.FindExtension() 27 | ?? new RalmsExOptionsExtension(); 28 | 29 | ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); 30 | 31 | return optionsBuilder; 32 | } 33 | 34 | public static DbContextOptionsBuilder RalmsExtendFunctions( 35 | this DbContextOptionsBuilder optionsBuilder) 36 | where TContext : DbContext 37 | => (DbContextOptionsBuilder)RalmsExtendFunctions((DbContextOptionsBuilder)optionsBuilder); 38 | 39 | #warning Use in the future! 40 | public static DbContextOptionsBuilder RalmsExtendSqlServer( 41 | this DbContextOptionsBuilder optionsBuilder) 42 | where TContext : DbContext 43 | => (DbContextOptionsBuilder)RalmsExtendFunctions((DbContextOptionsBuilder)optionsBuilder); 44 | 45 | public static DbContextOptionsBuilder RalmsExtendSqlite( 46 | this DbContextOptionsBuilder optionsBuilder) 47 | where TContext : DbContext 48 | => (DbContextOptionsBuilder)RalmsExtendFunctions((DbContextOptionsBuilder)optionsBuilder); 49 | 50 | public static DbContextOptionsBuilder RalmsExtendFirebird( 51 | this DbContextOptionsBuilder optionsBuilder) 52 | where TContext : DbContext 53 | => (DbContextOptionsBuilder)RalmsExtendFunctions((DbContextOptionsBuilder)optionsBuilder); 54 | 55 | public static DbContextOptionsBuilder RalmsExtendPostgres( 56 | this DbContextOptionsBuilder optionsBuilder) 57 | where TContext : DbContext 58 | => (DbContextOptionsBuilder)RalmsExtendFunctions((DbContextOptionsBuilder)optionsBuilder); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Extensions/RalmsServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query; 18 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 19 | using Microsoft.EntityFrameworkCore.Query.Sql; 20 | using Ralms.EntityFrameworkCore.Extensions; 21 | using Ralms.EntityFrameworkCore.Extensions.With; 22 | 23 | namespace Microsoft.Extensions.DependencyInjection 24 | { 25 | public static class RalmsServiceCollectionExtensions 26 | { 27 | public static IServiceCollection AddRalmsExtensions(this IServiceCollection services) 28 | { 29 | return services 30 | .AddSingleton() 31 | .AddScoped() 32 | .AddScoped(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Query/Operators/WithExpressionNode.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System.Collections.Generic; 18 | using System.Linq.Expressions; 19 | using System.Reflection; 20 | using Remotion.Linq.Clauses; 21 | using Remotion.Linq.Parsing.Structure.IntermediateModel; 22 | 23 | namespace Microsoft.EntityFrameworkCore.Query.ResultOperators.Internal 24 | { 25 | public class WithHintExpressionNode : ResultOperatorExpressionNodeBase 26 | { 27 | public static readonly IReadOnlyCollection SupportedMethods = new[] 28 | { RalmsQueryableExtensions.WithHintMethodInfo }; 29 | 30 | private readonly ConstantExpression _withHintExpression; 31 | 32 | public WithHintExpressionNode( 33 | MethodCallExpressionParseInfo parseInfo, 34 | ConstantExpression withNoLockExpressionExpression) 35 | : base(parseInfo, null, null) 36 | => _withHintExpression = withNoLockExpressionExpression; 37 | 38 | protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) 39 | => new WithHintResultOperator((string)_withHintExpression.Value); 40 | 41 | public override Expression Resolve( 42 | ParameterExpression inputParameter, 43 | Expression expressionToBeResolved, 44 | ClauseGenerationContext clauseGenerationContext) 45 | => Source.Resolve(inputParameter, expressionToBeResolved, clauseGenerationContext); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Query/Operators/WithResultOperator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System; 18 | using System.Linq.Expressions; 19 | using Remotion.Linq; 20 | using Remotion.Linq.Clauses; 21 | using Remotion.Linq.Clauses.ResultOperators; 22 | using Remotion.Linq.Clauses.StreamedData; 23 | 24 | namespace Microsoft.EntityFrameworkCore.Query.ResultOperators.Internal 25 | { 26 | public class WithHintResultOperator : SequenceTypePreservingResultOperatorBase, IQueryAnnotation 27 | { 28 | public WithHintResultOperator(string hint) 29 | { 30 | Hint = hint; 31 | } 32 | 33 | public virtual string Hint { get; } 34 | public virtual IQuerySource QuerySource { get; set; } 35 | public virtual QueryModel QueryModel { get; set; } 36 | public override string ToString() => !string.IsNullOrWhiteSpace(Hint) ? $"WITH ({Hint})" : ""; 37 | public override ResultOperatorBase Clone(CloneContext cloneContext) 38 | => new WithHintResultOperator(Hint); 39 | 40 | public override void TransformExpressions(Func transformation) 41 | { 42 | } 43 | 44 | public override StreamedSequence ExecuteInMemory(StreamedSequence input) => input; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Query/QueryGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System.Linq; 18 | using System.Linq.Expressions; 19 | using Microsoft.EntityFrameworkCore.Query.Expressions; 20 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 21 | using Microsoft.EntityFrameworkCore.Query.Sql; 22 | using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal; 23 | using Microsoft.EntityFrameworkCore.Storage; 24 | using Ralms.EntityFrameworkCore.Extensions.With.Query; 25 | 26 | namespace Ralms.EntityFrameworkCore.Extensions 27 | { 28 | public class QueryGenerator : SqlServerQuerySqlGenerator 29 | { 30 | public QueryGenerator( 31 | QuerySqlGeneratorDependencies dependencies, 32 | SelectExpression selectExpression, 33 | bool rowNumberPagingEnabled) 34 | : base(dependencies, selectExpression, rowNumberPagingEnabled) 35 | { 36 | } 37 | 38 | public override Expression VisitTable(TableExpression tableExpression) 39 | { 40 | var table = tableExpression as TableExpressionExtension; 41 | 42 | var visitTable = base.VisitTable(table); 43 | 44 | if (!string.IsNullOrWhiteSpace(table.Hint)) 45 | { 46 | Sql.Append($" WITH ({table.Hint}) "); 47 | } 48 | 49 | return visitTable; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Query/QueryGeneratorFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query.Expressions; 18 | using Microsoft.EntityFrameworkCore.Query.Sql; 19 | using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; 20 | 21 | namespace Ralms.EntityFrameworkCore.Extensions 22 | { 23 | public class QueryGeneratorFactory : QuerySqlGeneratorFactoryBase 24 | { 25 | private readonly ISqlServerOptions _sqlServerOptions; 26 | public QueryGeneratorFactory( 27 | QuerySqlGeneratorDependencies dependencies, 28 | ISqlServerOptions sqlServerOptions) 29 | : base(dependencies) 30 | { 31 | _sqlServerOptions = sqlServerOptions; 32 | } 33 | 34 | public override IQuerySqlGenerator CreateDefault(SelectExpression selectExpression) 35 | => new QueryGenerator( 36 | Dependencies, 37 | selectExpression, 38 | _sqlServerOptions.RowNumberPagingEnabled); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/Query/TableExpressionExtension.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query.Expressions; 18 | using Remotion.Linq.Clauses; 19 | 20 | namespace Ralms.EntityFrameworkCore.Extensions.With.Query 21 | { 22 | public class TableExpressionExtension : TableExpression 23 | { 24 | public virtual string Hint { get; } 25 | public TableExpressionExtension( 26 | string table, 27 | string schema, 28 | string alias, 29 | string withHint, 30 | IQuerySource querySource) 31 | :base(table,schema,alias,querySource) 32 | { 33 | Hint = withHint; 34 | } 35 | 36 | public override int GetHashCode() 37 | { 38 | unchecked 39 | { 40 | return GetType().GetHashCode() ^ (base.GetHashCode() * 397); 41 | } 42 | } 43 | 44 | public override bool Equals(object obj) 45 | { 46 | if (obj is null) 47 | { 48 | return false; 49 | } 50 | 51 | if (ReferenceEquals(this, obj)) 52 | { 53 | return true; 54 | } 55 | 56 | return obj.GetType() == GetType() 57 | && Equals((TableExpressionExtension)obj); 58 | } 59 | 60 | private bool Equals(TableExpressionExtension oldExpression) 61 | => string.Equals(Table, oldExpression.Table) 62 | && string.Equals(Schema, oldExpression.Schema) 63 | && string.Equals(Alias, oldExpression.Alias) 64 | && Equals(QuerySource, oldExpression.QuerySource); 65 | 66 | public override string ToString() 67 | => Table + " " + Alias + (!string.IsNullOrWhiteSpace(Hint) ? $" WITH ({Hint})" : ""); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/RalmsCompilationQueryableFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query.Internal; 18 | using Microsoft.EntityFrameworkCore.Query.ResultOperators.Internal; 19 | 20 | namespace Microsoft.EntityFrameworkCore.Query 21 | { 22 | public class RalmsCompilationQueryableFactory : QueryCompilationContextFactory 23 | { 24 | public RalmsCompilationQueryableFactory( 25 | QueryCompilationContextDependencies dependencies, 26 | RelationalQueryCompilationContextDependencies relationalDependencies) 27 | : base(dependencies) 28 | { 29 | relationalDependencies 30 | .NodeTypeProviderFactory 31 | .RegisterMethods(WithHintExpressionNode.SupportedMethods, typeof(WithHintExpressionNode)); 32 | } 33 | 34 | public override QueryCompilationContext Create(bool async) 35 | => async 36 | ? new RelationalQueryCompilationContext( 37 | Dependencies, 38 | new AsyncLinqOperatorProvider(), 39 | new AsyncQueryMethodProvider(), 40 | TrackQueryResults) 41 | : new RelationalQueryCompilationContext( 42 | Dependencies, 43 | new LinqOperatorProvider(), 44 | new QueryMethodProvider(), 45 | TrackQueryResults); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/RalmsEntityQueryableExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore; 18 | using Microsoft.EntityFrameworkCore.Internal; 19 | using Microsoft.EntityFrameworkCore.Metadata; 20 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 21 | using Microsoft.EntityFrameworkCore.Query; 22 | using Microsoft.EntityFrameworkCore.Query.Expressions; 23 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 24 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal; 25 | using Microsoft.EntityFrameworkCore.Query.Internal; 26 | using Microsoft.EntityFrameworkCore.Query.ResultOperators.Internal; 27 | using Microsoft.EntityFrameworkCore.Query.Sql; 28 | using Microsoft.EntityFrameworkCore.Storage; 29 | using Ralms.EntityFrameworkCore.Extensions.With.Query; 30 | using Remotion.Linq.Clauses; 31 | using System; 32 | using System.Collections.Generic; 33 | using System.Linq; 34 | using System.Linq.Expressions; 35 | using System.Reflection; 36 | 37 | namespace Ralms.EntityFrameworkCore.Extensions.With 38 | { 39 | public class RalmsEntityQueryableExpressionVisitor : EntityQueryableExpressionVisitor 40 | { 41 | private readonly IModel _model; 42 | private readonly ISelectExpressionFactory _selectExpressionFactory; 43 | private readonly IMaterializerFactory _materializerFactory; 44 | private readonly IShaperCommandContextFactory _shaperCommandContextFactory; 45 | private readonly IQuerySource _querySource; 46 | 47 | private new RelationalQueryModelVisitor QueryModelVisitor 48 | => (RelationalQueryModelVisitor)base.QueryModelVisitor; 49 | 50 | public RalmsEntityQueryableExpressionVisitor( 51 | RelationalEntityQueryableExpressionVisitorDependencies dependencies, 52 | RelationalQueryModelVisitor queryModelVisitor, 53 | IQuerySource querySource) 54 | : base(entityQueryModelVisitor: queryModelVisitor) 55 | { 56 | _model = dependencies.Model; 57 | _selectExpressionFactory = dependencies.SelectExpressionFactory; 58 | _materializerFactory = dependencies.MaterializerFactory; 59 | _shaperCommandContextFactory = dependencies.ShaperCommandContextFactory; 60 | _querySource = querySource; 61 | } 62 | 63 | protected override Expression VisitEntityQueryable(Type elementType) 64 | { 65 | var relationalQueryCompilationContext = QueryModelVisitor.QueryCompilationContext; 66 | 67 | var entityType = relationalQueryCompilationContext.FindEntityType(_querySource) 68 | ?? _model.FindEntityType(elementType); 69 | 70 | var selectExpression = _selectExpressionFactory.Create(relationalQueryCompilationContext); 71 | 72 | QueryModelVisitor.AddQuery(_querySource, selectExpression); 73 | 74 | var tableName = entityType.Relational().TableName; 75 | 76 | var tableAlias 77 | = relationalQueryCompilationContext.CreateUniqueTableAlias( 78 | _querySource.HasGeneratedItemName() 79 | ? tableName[0].ToString().ToLowerInvariant() 80 | : (_querySource as GroupJoinClause)?.JoinClause.ItemName 81 | ?? _querySource.ItemName); 82 | 83 | var fromSqlAnnotation 84 | = relationalQueryCompilationContext 85 | .QueryAnnotations 86 | .OfType() 87 | .LastOrDefault(a => a.QuerySource == _querySource); 88 | 89 | var withHintAnnotation 90 | = relationalQueryCompilationContext 91 | .QueryAnnotations 92 | .OfType() 93 | .LastOrDefault(a => !string.IsNullOrWhiteSpace(a.Hint)); 94 | 95 | var includes 96 | = relationalQueryCompilationContext 97 | .QueryAnnotations 98 | .OfType() 99 | .LastOrDefault(a => a.QuerySource == _querySource); 100 | 101 | Func querySqlGeneratorFunc = selectExpression.CreateDefaultQuerySqlGenerator; 102 | 103 | if (fromSqlAnnotation == null) 104 | { 105 | selectExpression.AddTable( 106 | new TableExpressionExtension( 107 | tableName, 108 | entityType.Relational().Schema, 109 | tableAlias, 110 | withHintAnnotation?.Hint, 111 | _querySource)); 112 | } 113 | else 114 | { 115 | selectExpression.AddTable( 116 | new FromSqlExpression( 117 | fromSqlAnnotation.Sql, 118 | fromSqlAnnotation.Arguments, 119 | tableAlias, 120 | _querySource)); 121 | 122 | var trimmedSql = fromSqlAnnotation.Sql.TrimStart('\r', '\n', '\t', ' '); 123 | 124 | var useQueryComposition 125 | = trimmedSql.StartsWith("SELECT ", StringComparison.OrdinalIgnoreCase) 126 | || trimmedSql.StartsWith("SELECT" + Environment.NewLine, StringComparison.OrdinalIgnoreCase) 127 | || trimmedSql.StartsWith("SELECT\t", StringComparison.OrdinalIgnoreCase); 128 | 129 | var requiresClientEval = !useQueryComposition; 130 | 131 | if (!useQueryComposition) 132 | { 133 | if (relationalQueryCompilationContext.IsIncludeQuery) 134 | { 135 | throw new InvalidOperationException( 136 | RelationalStrings.StoredProcedureIncludeNotSupported); 137 | } 138 | } 139 | 140 | if (useQueryComposition 141 | && fromSqlAnnotation.QueryModel.IsIdentityQuery() 142 | && !fromSqlAnnotation.QueryModel.ResultOperators.Any() 143 | && !relationalQueryCompilationContext.IsIncludeQuery 144 | && entityType.BaseType == null 145 | && !entityType.GetDerivedTypes().Any()) 146 | { 147 | useQueryComposition = false; 148 | } 149 | 150 | if (!useQueryComposition) 151 | { 152 | QueryModelVisitor.RequiresClientEval = requiresClientEval; 153 | 154 | querySqlGeneratorFunc = () 155 | => selectExpression.CreateFromSqlQuerySqlGenerator( 156 | fromSqlAnnotation.Sql, 157 | fromSqlAnnotation.Arguments); 158 | } 159 | } 160 | 161 | var shaper = CreateShaper(elementType, entityType, selectExpression); 162 | 163 | DiscriminateProjectionQuery(entityType, selectExpression, _querySource); 164 | 165 | return Expression.Call( 166 | QueryModelVisitor.QueryCompilationContext.QueryMethodProvider 167 | .ShapedQueryMethod 168 | .MakeGenericMethod(shaper.Type), 169 | EntityQueryModelVisitor.QueryContextParameter, 170 | Expression.Constant(_shaperCommandContextFactory.Create(querySqlGeneratorFunc)), 171 | Expression.Constant(shaper)); 172 | } 173 | 174 | private Shaper CreateShaper(Type elementType, IEntityType entityType, SelectExpression selectExpression) 175 | { 176 | Shaper shaper; 177 | 178 | if (QueryModelVisitor.QueryCompilationContext 179 | .QuerySourceRequiresMaterialization(_querySource) 180 | || QueryModelVisitor.RequiresClientEval) 181 | { 182 | var materializerExpression 183 | = _materializerFactory 184 | .CreateMaterializer( 185 | entityType, 186 | selectExpression, 187 | (p, se) => 188 | se.AddToProjection( 189 | p, 190 | _querySource), 191 | out var typeIndexMap); 192 | 193 | var materializer = materializerExpression.Compile(); 194 | 195 | shaper 196 | = (Shaper)_createEntityShaperMethodInfo.MakeGenericMethod(elementType) 197 | .Invoke( 198 | obj: null, 199 | parameters: new object[] 200 | { 201 | _querySource, 202 | QueryModelVisitor.QueryCompilationContext.IsTrackingQuery 203 | && !entityType.IsQueryType, 204 | entityType.FindPrimaryKey(), 205 | materializer, 206 | materializerExpression, 207 | typeIndexMap, 208 | QueryModelVisitor.QueryCompilationContext.IsQueryBufferRequired 209 | && !entityType.IsQueryType 210 | }); 211 | } 212 | else 213 | { 214 | shaper = new ValueBufferShaper(_querySource); 215 | } 216 | 217 | return shaper; 218 | } 219 | 220 | private static readonly MethodInfo _createEntityShaperMethodInfo 221 | = typeof(RelationalEntityQueryableExpressionVisitor).GetTypeInfo() 222 | .GetDeclaredMethod(nameof(CreateEntityShaper)); 223 | 224 | private static IShaper CreateEntityShaper( 225 | IQuerySource querySource, 226 | bool trackingQuery, 227 | IKey key, 228 | Func materializer, 229 | Expression materializerExpression, 230 | Dictionary typeIndexMap, 231 | bool useQueryBuffer) 232 | where TEntity : class 233 | { 234 | return !useQueryBuffer 235 | ? (IShaper)new UnbufferedEntityShaper( 236 | querySource, 237 | trackingQuery, 238 | key, 239 | materializer, 240 | materializerExpression) 241 | : new BufferedEntityShaper( 242 | querySource, 243 | trackingQuery, 244 | key, 245 | materializer, 246 | typeIndexMap); 247 | } 248 | 249 | private void DiscriminateProjectionQuery( 250 | IEntityType entityType, 251 | SelectExpression selectExpression, 252 | IQuerySource querySource) 253 | { 254 | Expression discriminatorPredicate; 255 | 256 | if (entityType.IsQueryType) 257 | { 258 | discriminatorPredicate = GenerateDiscriminatorExpression(entityType, selectExpression, querySource); 259 | } 260 | else 261 | { 262 | var sharedTypes = new HashSet( 263 | _model.GetEntityTypes() 264 | .Where(e => !e.IsQueryType) 265 | .Where( 266 | et => et.Relational().TableName == entityType.Relational().TableName 267 | && et.Relational().Schema == entityType.Relational().Schema)); 268 | 269 | var currentPath = new Stack(); 270 | currentPath.Push(entityType); 271 | 272 | var allPaths = new List>(); 273 | FindPaths(entityType.RootType(), sharedTypes, currentPath, allPaths); 274 | 275 | discriminatorPredicate = allPaths 276 | .Select( 277 | p => p.Select( 278 | et => GenerateDiscriminatorExpression(et, selectExpression, querySource)) 279 | .Aggregate( 280 | (Expression)null, 281 | (result, current) => result != null 282 | ? current != null 283 | ? Expression.AndAlso(result, current) 284 | : result 285 | : current)) 286 | .Aggregate( 287 | (Expression)null, 288 | (result, current) => result != null 289 | ? current != null 290 | ? Expression.OrElse(result, current) 291 | : result 292 | : current); 293 | } 294 | 295 | if (discriminatorPredicate != null) 296 | { 297 | selectExpression.Predicate = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); 298 | } 299 | } 300 | 301 | private static void FindPaths( 302 | IEntityType entityType, ICollection sharedTypes, 303 | Stack currentPath, ICollection> result) 304 | { 305 | var identifyingFks = entityType.FindForeignKeys(entityType.FindPrimaryKey().Properties) 306 | .Where( 307 | fk => fk.PrincipalKey.IsPrimaryKey() 308 | && fk.PrincipalEntityType != entityType 309 | && sharedTypes.Contains(fk.PrincipalEntityType)) 310 | .ToList(); 311 | 312 | if (identifyingFks.Count == 0) 313 | { 314 | result.Add(new List(currentPath)); 315 | return; 316 | } 317 | 318 | foreach (var fk in identifyingFks) 319 | { 320 | currentPath.Push(fk.PrincipalEntityType); 321 | FindPaths(fk.PrincipalEntityType.RootType(), sharedTypes, currentPath, result); 322 | currentPath.Pop(); 323 | } 324 | } 325 | 326 | private static Expression GenerateDiscriminatorExpression( 327 | IEntityType entityType, 328 | SelectExpression selectExpression, 329 | IQuerySource querySource) 330 | { 331 | var concreteEntityTypes 332 | = entityType.GetConcreteTypesInHierarchy().ToList(); 333 | 334 | if (concreteEntityTypes.Count == 1 335 | && concreteEntityTypes[0].RootType() == concreteEntityTypes[0]) 336 | { 337 | return null; 338 | } 339 | 340 | var discriminatorColumn 341 | = selectExpression.BindProperty( 342 | concreteEntityTypes[0].Relational().DiscriminatorProperty, 343 | querySource); 344 | 345 | var firstDiscriminatorValue 346 | = Expression.Constant( 347 | concreteEntityTypes[0].Relational().DiscriminatorValue, 348 | discriminatorColumn.Type); 349 | 350 | var discriminatorPredicate 351 | = Expression.Equal(discriminatorColumn, firstDiscriminatorValue); 352 | 353 | if (concreteEntityTypes.Count > 1) 354 | { 355 | discriminatorPredicate 356 | = concreteEntityTypes 357 | .Skip(1) 358 | .Select( 359 | concreteEntityType 360 | => Expression.Constant( 361 | concreteEntityType.Relational().DiscriminatorValue, 362 | discriminatorColumn.Type)) 363 | .Aggregate( 364 | discriminatorPredicate, (current, discriminatorValue) => 365 | Expression.OrElse( 366 | Expression.Equal(discriminatorColumn, discriminatorValue), 367 | current)); 368 | } 369 | 370 | return discriminatorPredicate; 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/RalmsEntityQueryableExpressionVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Query; 18 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 19 | using Remotion.Linq.Clauses; 20 | using System.Linq.Expressions; 21 | 22 | namespace Ralms.EntityFrameworkCore.Extensions.With 23 | { 24 | public class RalmsEntityQueryableExpressionVisitorFactory : IEntityQueryableExpressionVisitorFactory 25 | { 26 | public RalmsEntityQueryableExpressionVisitorFactory( 27 | RelationalEntityQueryableExpressionVisitorDependencies dependencies) 28 | => Dependencies = dependencies; 29 | 30 | protected virtual RelationalEntityQueryableExpressionVisitorDependencies Dependencies { get; } 31 | 32 | public virtual ExpressionVisitor Create( 33 | EntityQueryModelVisitor queryModelVisitor, IQuerySource querySource) 34 | => new RalmsEntityQueryableExpressionVisitor( 35 | Dependencies, 36 | (RelationalQueryModelVisitor)queryModelVisitor, 37 | querySource); 38 | } 39 | } -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/RalmsExOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore.Infrastructure; 18 | using Microsoft.Extensions.DependencyInjection; 19 | 20 | namespace Ralms.EntityFrameworkCore.Extensions 21 | { 22 | class RalmsExOptionsExtension : IDbContextOptionsExtension 23 | { 24 | public string LogFragment => string.Empty; 25 | 26 | public long GetServiceProviderHashCode() 27 | => base.GetHashCode() * 397; 28 | 29 | public void Validate(IDbContextOptions options) 30 | { 31 | } 32 | 33 | public bool ApplyServices(IServiceCollection services) 34 | { 35 | services.AddRalmsExtensions(); 36 | return false; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/RalmsQueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System; 18 | using System.Linq; 19 | using System.Linq.Expressions; 20 | using System.Reflection; 21 | using Microsoft.EntityFrameworkCore.Infrastructure; 22 | using Microsoft.EntityFrameworkCore.Internal; 23 | using Microsoft.EntityFrameworkCore.Query; 24 | 25 | namespace Microsoft.EntityFrameworkCore 26 | { 27 | public static class RalmsQueryableExtensions 28 | { 29 | #region WithHint 30 | internal static readonly MethodInfo WithHintMethodInfo 31 | = typeof(RalmsQueryableExtensions) 32 | .GetTypeInfo().GetDeclaredMethods(nameof(WithHint)) 33 | .Single(); 34 | 35 | public static IQueryable WithHint( 36 | this IQueryable source, 37 | [NotParameterized] string hint) 38 | where TEntity : class 39 | { 40 | var infrastructure = source as IInfrastructure; 41 | var serviceProvider = infrastructure.Instance; 42 | var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) 43 | as ICurrentDbContext; 44 | var providerName = currentDbContext.Context.Database.ProviderName; 45 | 46 | if (providerName != "Microsoft.EntityFrameworkCore.SqlServer") 47 | return source; 48 | 49 | return source.Provider.CreateQuery( 50 | Expression.Call( 51 | null, 52 | WithHintMethodInfo.MakeGenericMethod(typeof(TEntity)), 53 | source.Expression, 54 | Expression.Constant(hint, typeof(string)))); 55 | } 56 | #endregion 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Extensions/WithHint/SqlServerHints.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | namespace Microsoft.EntityFrameworkCore 18 | { 19 | public static class SqlServerHints 20 | { 21 | public static readonly string FORCESCAN = "FORCESCAN"; 22 | public static readonly string HOLDLOCK = "HOLDLOCK"; 23 | public static readonly string NOLOCK = "NOLOCK"; 24 | public static readonly string NOWAIT = "NOWAIT"; 25 | public static readonly string PAGLOCK = "PAGLOCK"; 26 | public static readonly string READCOMMITTED = "READCOMMITTED"; 27 | public static readonly string READCOMMITTEDLOCK = "READCOMMITTEDLOCK"; 28 | public static readonly string READPAST = "READPAST"; 29 | public static readonly string READUNCOMMITTED = "READUNCOMMITTED"; 30 | public static readonly string REPEATABLEREAD = "REPEATABLEREAD"; 31 | public static readonly string ROWLOCK = "ROWLOCK"; 32 | public static readonly string SERIALIZABLE = "SERIALIZABLE"; 33 | public static readonly string SNAPSHOT = "SNAPSHOT"; 34 | public static readonly string TABLOCK = "TABLOCK"; 35 | public static readonly string UPDLOCK = "UPDLOCK"; 36 | public static readonly string XLOCK = "XLOCK"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Tests/Other/Model.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | namespace Ralms.EntityFrameworkCore.Tests 21 | { 22 | public class Blog 23 | { 24 | public int Id { get; set; } 25 | public string Name { get; set; } 26 | public DateTime Date { get; set; } 27 | public virtual IEnumerable Posts { get; set; } 28 | } 29 | 30 | public class Post 31 | { 32 | public int Id { get; set; } 33 | public string Title { get; set; } 34 | public string Content { get; set; } 35 | 36 | public virtual Blog Blog { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Tests/Other/SampleContext.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore; 18 | using Microsoft.Extensions.Logging; 19 | 20 | namespace Ralms.EntityFrameworkCore.Tests 21 | { 22 | public class SampleContext : DbContext 23 | { 24 | private readonly string NameDatabase = ""; 25 | public DbSet Blogs { get; set; } 26 | public DbSet Posts { get; set; } 27 | 28 | public SampleContext(string database) 29 | { 30 | NameDatabase = database; 31 | } 32 | 33 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 34 | { 35 | optionsBuilder 36 | .UseSqlServer($"Server=.\\SQL2017;Database={NameDatabase};Integrated Security=True;") 37 | .RalmsExtendFunctions() 38 | .UseLoggerFactory(_loggerFactory) 39 | .EnableSensitiveDataLogging(); 40 | } 41 | 42 | protected override void OnModelCreating(ModelBuilder modelo) 43 | { 44 | modelo.EnableSqlServerDateDIFF(); 45 | } 46 | 47 | private static readonly ILoggerFactory _loggerFactory = new LoggerFactory() 48 | .AddDebug() 49 | .AddConsole((s, l) => l == LogLevel.Information && !s.EndsWith("Connection")); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Tests/Ralms.EntityFrameworkCore.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net461 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Tests/TestDataDiff.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore; 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Xunit; 22 | 23 | namespace Ralms.EntityFrameworkCore.Tests 24 | { 25 | public class TestDataDiff 26 | { 27 | private SampleContext _db; 28 | private List _blogList; 29 | 30 | public TestDataDiff() 31 | { 32 | _db = new SampleContext("DateDiff"); 33 | _blogList = new List(); 34 | _db.Database.EnsureCreated(); 35 | 36 | for (int i = 0; i < 100; i++) 37 | { 38 | _blogList.Add(new Blog 39 | { 40 | Name = $"Teste {i}", 41 | Date = DateTime.Now, 42 | Posts = new[] 43 | { 44 | new Post 45 | { 46 | Content = $"Test Content {i}", 47 | Title = $"Title Post {i}" 48 | } 49 | } 50 | }); 51 | } 52 | 53 | _db.Blogs.AddRange(_blogList); 54 | _db.SaveChanges(); 55 | } 56 | 57 | [Fact] 58 | public void ClientEval() 59 | { 60 | var list = _blogList 61 | .Where(p => EFCore.DateDiff(DatePart.day, DateTimeOffset.Now, p.Date) < 50) 62 | .Take(50) 63 | .ToList(); 64 | 65 | Assert.True(list.Count == 50); 66 | } 67 | 68 | [Fact] 69 | public void ServerTranslate() 70 | { 71 | var sql = _db 72 | .Blogs 73 | .Where(p => EFCore.DateDiff(DatePart.day, DateTime.Now, p.Date) < 50) 74 | .ToSql(); 75 | 76 | Assert.Contains("DATEDIFF",sql); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Ralms.EntityFrameworkCore.Tests/TestWithHint.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Rafael Almeida (ralms@ralms.net) 3 | * 4 | * Ralms.Microsoft.EntitityFrameworkCore.Extensions 5 | * 6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED 7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 8 | * 9 | * Permission is hereby granted to use or copy this program 10 | * for any purpose, provided the above notices are retained on all copies. 11 | * Permission to modify the code and to distribute modified code is granted, 12 | * provided the above notices are retained, and a notice that the code was 13 | * modified is included with the above copyright notice. 14 | * 15 | */ 16 | 17 | using Microsoft.EntityFrameworkCore; 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Xunit; 22 | 23 | namespace Ralms.EntityFrameworkCore.Tests 24 | { 25 | public class TestWithHint 26 | { 27 | private readonly SampleContext _db; 28 | private readonly List _blogList; 29 | 30 | public TestWithHint() 31 | { 32 | _db = new SampleContext("WithLock"); 33 | _blogList = new List(); 34 | _db.Database.EnsureDeleted(); 35 | _db.Database.EnsureCreated(); 36 | 37 | for (int i = 0; i < 100; i++) 38 | { 39 | _blogList.Add(new Blog 40 | { 41 | Name = $"Teste {i}", 42 | Date = DateTime.Now, 43 | Posts = new[] 44 | { 45 | new Post 46 | { 47 | Content = $"Test Content {i}", 48 | Title = $"Title Post {i}" 49 | } 50 | } 51 | }); 52 | } 53 | 54 | _db.Blogs.AddRange(_blogList); 55 | _db.SaveChanges(); 56 | } 57 | 58 | [Theory] 59 | [InlineData(nameof(SqlServerHints.PAGLOCK))] 60 | [InlineData(nameof(SqlServerHints.NOLOCK))] 61 | [InlineData(nameof(SqlServerHints.HOLDLOCK))] 62 | [InlineData(nameof(SqlServerHints.NOWAIT))] 63 | [InlineData(nameof(SqlServerHints.ROWLOCK))] 64 | [InlineData(nameof(SqlServerHints.XLOCK))] 65 | [InlineData(nameof(SqlServerHints.UPDLOCK))] 66 | [InlineData("")] 67 | public void Test_wih_no_lock(string hint) 68 | { 69 | var query = _db 70 | .Blogs 71 | .Include(p => p.Posts) 72 | .WithHint(hint) 73 | .Skip(5) 74 | .Take(10) 75 | .ToSql(); 76 | 77 | if (!string.IsNullOrWhiteSpace(hint)) 78 | { 79 | Assert.Contains($"WITH ({hint})", query); 80 | } 81 | else 82 | { 83 | Assert.DoesNotContain($"WITH ({hint})", query); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 Preview 3 | services: mssql2017 4 | init: 5 | - git config --global core.autocrlf input 6 | cache: 7 | - '%USERPROFILE%\.nuget\packages -> **\*.csproj' 8 | shallow_clone: true 9 | install: 10 | - ps: $env:padded_build_number = $env:appveyor_build_number.PadLeft(5, '0') 11 | - ps: Invoke-WebRequest -Uri "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1" -OutFile "install-dotnet.ps1" 12 | - ps: .\install-dotnet.ps1 -Version 2.1.300 13 | test: off 14 | before_build: 15 | - ps: $baseDir = Resolve-Path . 16 | - dotnet --info 17 | - appveyor-retry dotnet restore -v Minimal 18 | build: 19 | verbosity: minimal --------------------------------------------------------------------------------