├── .gitattributes
├── .github
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── AutoMapper.Extensions.ExpressionMapping.sln
├── AutoMapper.snk
├── Directory.Build.props
├── LICENSE
├── Pack_Push.ps1
├── README.md
├── icon.png
├── src
└── AutoMapper.Extensions.ExpressionMapping
│ ├── AnonymousTypeFactory.cs
│ ├── AutoMapper.Extensions.ExpressionMapping.csproj
│ ├── ConfigurationExtensions.cs
│ ├── ElementTypeHelper.cs
│ ├── ExpressionExtensions.cs
│ ├── ExpressionMapper.cs
│ ├── Extensions
│ └── VisitorExtensions.cs
│ ├── FindMemberExpressionsVisitor.cs
│ ├── Impl
│ ├── ISourceInjectedQueryable.cs
│ ├── QueryDataSourceInjection.cs
│ ├── SourceInjectedQuery.cs
│ ├── SourceInjectedQueryInspector.cs
│ └── SourceInjectedQueryProvider.cs
│ ├── LockingConcurrentDictionary.cs
│ ├── MapIncludesVisitor.cs
│ ├── MapperExtensions.cs
│ ├── MapperInfoDictionary.cs
│ ├── MemberVisitor.cs
│ ├── NullsafeQueryRewriter.cs
│ ├── ParameterExpressionEqualityComparer.cs
│ ├── PrependParentNameVisitor.cs
│ ├── Properties
│ ├── Resources.Designer.cs
│ └── Resources.resx
│ ├── QueryableExtensions.cs
│ ├── ReflectionExtensions.cs
│ ├── ReplaceExpressionVisitor.cs
│ ├── Structures
│ ├── DeclaringMemberKey.cs
│ ├── MapperInfo.cs
│ ├── MemberAssignmentInfo.cs
│ ├── MemberBindingGroup.cs
│ └── PropertyMapInfo.cs
│ ├── TypeExtensions.cs
│ ├── TypeMapHelper.cs
│ └── XpressionMapperVisitor.cs
└── tests
└── AutoMapper.Extensions.ExpressionMapping.UnitTests
├── AssertionExtensions.cs
├── AutoMapper.Extensions.ExpressionMapping.UnitTests.csproj
├── AutoMapperSpecBase.cs
├── CanMapExpressionWithListConstants.cs
├── CanMapExpressionWithLocalExpressionConstant.cs
├── CanMapMemberFromTypeBinaryExpression.cs
├── CanMapMismatchedLiteralMemberExpressionsWithoutCustomExpressions.cs
├── CanMapParameterBodyFromChildReferenceWithoutMemberExpression.cs
├── CanMapParameterBodyWithoutMemberExpression.cs
├── EF.cs
├── EnumerableDotContainsWorksOnLocalVariables.cs
├── ExplicitExpansionAsDataSource.cs
├── ExpressionConversion.cs
├── ExpressionMapping.cs
├── ExpressionMappingEnumToNumericOrString.cs
├── ExpressionMappingPropertyFromBaseClass.cs
├── ExpressionMappingPropertyFromDerviedType.cs
├── ExpressionMappingWithUseAsDataSource.cs
├── GenericTestExtensionMethods.cs
├── Impl
└── SourceInjectedQuery.cs
├── MappingMemberInitWithCustomExpressions.cs
├── MappingMemberInitWithPropertiesInBaseClass.cs
├── MappingWithIncludeMembersConfigurations.cs
├── MemberMappingsOfLiteralParentTypesMustMatch.cs
├── ParameterizedQueriesTestsAsDataSource.cs
├── ShouldOnlyMapExistingTypeMaps.cs
├── ShouldThrowInvalidOperationExceptionForUnmatchedLiterals.cs
├── ShouldUseDeclaringTypeForInstanceMethodCalls.cs
├── XpressionMapper.ForPath.Tests.cs
├── XpressionMapper.Structs.Tests.cs
└── XpressionMapperTests.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.doc diff=astextplain
2 | *.DOC diff=astextplain
3 | *.docx diff=astextplain
4 | *.DOCX diff=astextplain
5 | *.dot diff=astextplain
6 | *.DOT diff=astextplain
7 | *.pdf diff=astextplain
8 | *.PDF diff=astextplain
9 | *.rtf diff=astextplain
10 | *.RTF diff=astextplain
11 |
12 | *.jpg binary
13 | *.png binary
14 | *.gif binary
15 |
16 | core.eol crlf
17 |
18 | *.cs diff=csharp
19 |
20 | *.csproj merge=union
21 | *.vbproj merge=union
22 | *.fsproj merge=union
23 | *.dbproj merge=union
24 | *.sln merge=union
25 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: windows-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Add AutoMapper Myget Source
20 | run: dotnet nuget add source https://www.myget.org/F/automapperdev/api/v3/index.json -n automappermyget
21 |
22 | - name: Test
23 | run: dotnet test --configuration Release --verbosity normal
24 |
25 | - name: Pack and push
26 | env:
27 | PROJECT_NAME: AutoMapper.Extensions.ExpressionMapping
28 | DEPLOY_PACKAGE_URL: https://www.myget.org/F/automapperdev/api/v3/index.json
29 | DEPLOY_PACKAGE_API_KEY: ${{ secrets.MYGET_CI_API_KEY }}
30 | REPO: ${{ github.repository }}
31 | REPO_OWNER: ${{ github.repository_owner }}
32 | run: ./Pack_Push.ps1
33 | shell: pwsh
34 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: windows-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Test
18 | run: dotnet test --configuration Release --verbosity normal
19 |
20 | - name: Pack and push
21 | env:
22 | PROJECT_NAME: AutoMapper.Extensions.ExpressionMapping
23 | DEPLOY_PACKAGE_URL: https://api.nuget.org/v3/index.json
24 | DEPLOY_PACKAGE_API_KEY: ${{ secrets.NUGET_API_KEY }}
25 | REPO: ${{ github.repository }}
26 | REPO_OWNER: ${{ github.repository_owner }}
27 | run: ./Pack_Push.ps1
28 | shell: pwsh
29 |
--------------------------------------------------------------------------------
/.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 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 | artifacts/
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
28 | # Uncomment if you have tasks that create the project's static files in wwwroot
29 | #wwwroot/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.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 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 | *.ncrunchsolution
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Windows Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Windows Store app package directory
160 | AppPackages/
161 |
162 | # Visual Studio cache files
163 | # files ending in .cache can be ignored
164 | *.[Cc]ache
165 | # but keep track of directories ending in .cache
166 | !*.[Cc]ache/
167 |
168 | # Others
169 | ClientBin/
170 | [Ss]tyle[Cc]op.*
171 | ~$*
172 | *~
173 | *.dbmdl
174 | *.dbproj.schemaview
175 | *.pfx
176 | *.publishsettings
177 | node_modules/
178 | orleans.codegen.cs
179 |
180 | # RIA/Silverlight projects
181 | Generated_Code/
182 |
183 | # Backup & report files from converting an old project file
184 | # to a newer Visual Studio version. Backup files are not needed,
185 | # because we have git ;-)
186 | _UpgradeReport_Files/
187 | Backup*/
188 | UpgradeLog*.XML
189 | UpgradeLog*.htm
190 |
191 | # SQL Server files
192 | *.mdf
193 | *.ldf
194 |
195 | # Business Intelligence projects
196 | *.rdl.data
197 | *.bim.layout
198 | *.bim_*.settings
199 |
200 | # Microsoft Fakes
201 | FakesAssemblies/
202 |
203 | # Node.js Tools for Visual Studio
204 | .ntvs_analysis.dat
205 |
206 | # Visual Studio 6 build log
207 | *.plg
208 |
209 | # Visual Studio 6 workspace options file
210 | *.opt
211 |
212 | # Visual Studio LightSwitch build output
213 | **/*.HTMLClient/GeneratedArtifacts
214 | **/*.DesktopClient/GeneratedArtifacts
215 | **/*.DesktopClient/ModelManifest.xml
216 | **/*.Server/GeneratedArtifacts
217 | **/*.Server/ModelManifest.xml
218 | _Pvt_Extensions
219 |
220 | # Project Rider
221 | *.iml
222 | .idea
--------------------------------------------------------------------------------
/AutoMapper.Extensions.ExpressionMapping.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29001.49
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.Extensions.ExpressionMapping", "src\AutoMapper.Extensions.ExpressionMapping\AutoMapper.Extensions.ExpressionMapping.csproj", "{24DF305C-EE59-460A-BA97-4B7CD5505434}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.Extensions.ExpressionMapping.UnitTests", "tests\AutoMapper.Extensions.ExpressionMapping.UnitTests\AutoMapper.Extensions.ExpressionMapping.UnitTests.csproj", "{31D058FF-FD83-4DB3-9C32-1D3599687A8E}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{792DF9DF-A7ED-449E-B358-57953FC62B1D}"
11 | ProjectSection(SolutionItems) = preProject
12 | .gitattributes = .gitattributes
13 | .gitignore = .gitignore
14 | .github\workflows\ci.yml = .github\workflows\ci.yml
15 | Directory.Build.props = Directory.Build.props
16 | LICENSE = LICENSE
17 | Pack_Push.ps1 = Pack_Push.ps1
18 | README.md = README.md
19 | .github\workflows\release.yml = .github\workflows\release.yml
20 | EndProjectSection
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|Any CPU = Release|Any CPU
28 | Release|x64 = Release|x64
29 | Release|x86 = Release|x86
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|x64.ActiveCfg = Debug|Any CPU
35 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|x64.Build.0 = Debug|Any CPU
36 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|x86.ActiveCfg = Debug|Any CPU
37 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Debug|x86.Build.0 = Debug|Any CPU
38 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|x64.ActiveCfg = Release|Any CPU
41 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|x64.Build.0 = Release|Any CPU
42 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|x86.ActiveCfg = Release|Any CPU
43 | {24DF305C-EE59-460A-BA97-4B7CD5505434}.Release|x86.Build.0 = Release|Any CPU
44 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|x64.ActiveCfg = Debug|Any CPU
47 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|x64.Build.0 = Debug|Any CPU
48 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|x86.ActiveCfg = Debug|Any CPU
49 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Debug|x86.Build.0 = Debug|Any CPU
50 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|x64.ActiveCfg = Release|Any CPU
53 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|x64.Build.0 = Release|Any CPU
54 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|x86.ActiveCfg = Release|Any CPU
55 | {31D058FF-FD83-4DB3-9C32-1D3599687A8E}.Release|x86.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(ExtensibilityGlobals) = postSolution
61 | SolutionGuid = {7464B224-B1DB-4F63-89AE-34DDBF794E15}
62 | EndGlobalSection
63 | EndGlobal
64 |
--------------------------------------------------------------------------------
/AutoMapper.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutoMapper/AutoMapper.Extensions.ExpressionMapping/b8b4900d4331d2bb42487484621b42649094fcc4/AutoMapper.snk
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | Jimmy Bogard
4 | latest
5 | true
6 | $(NoWarn);1701;1702;1591
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 Jimmy Bogard
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Pack_Push.ps1:
--------------------------------------------------------------------------------
1 | $scriptName = $MyInvocation.MyCommand.Name
2 |
3 | Write-Host "Owner ${Env:REPO_OWNER}"
4 | Write-Host "Repository ${Env:REPO}"
5 |
6 | $PROJECT_PATH = ".\src\$($Env:PROJECT_NAME)\$($Env:PROJECT_NAME).csproj"
7 | $NUGET_PACKAGE_PATH = ".\artifacts\$($Env:PROJECT_NAME).*.nupkg"
8 |
9 | Write-Host "Project Path ${PROJECT_PATH}"
10 | Write-Host "Package Path ${NUGET_PACKAGE_PATH}"
11 |
12 | if ([string]::IsNullOrEmpty($Env:DEPLOY_PACKAGE_API_KEY)) {
13 | Write-Host "${scriptName}: Only creates packages on AutoMapper repositories."
14 | } else {
15 | dotnet pack $PROJECT_PATH -c Release -o .\artifacts --no-build
16 | dotnet nuget push $NUGET_PACKAGE_PATH --skip-duplicate --source $Env:DEPLOY_PACKAGE_URL --api-key $Env:DEPLOY_PACKAGE_API_KEY
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## OData
2 | AutoMapper extentions for mapping expressions (OData)
3 |
4 | [](https://www.nuget.org/packages/AutoMapper.Extensions.ExpressionMapping/)
5 |
6 | To use, configure using the configuration helper method:
7 |
8 | ```c#
9 | var mapper = new Mapper(new MapperConfiguration(cfg => {
10 | cfg.AddExpressionMapping();
11 | // Rest of your configuration
12 | }));
13 |
14 | // or if using the MS Ext DI:
15 |
16 | services.AddAutoMapper(cfg => {
17 | cfg.AddExpressionMapping();
18 | }, /* assemblies with profiles */);
19 | ```
20 |
21 | ## DTO Queries
22 | Expression Mapping also supports writing queries against the mapped objects. Take the following source and destination types:
23 | ```csharp
24 | public class User
25 | {
26 | public int Id { get; set; }
27 | public string Name { get; set; }
28 | }
29 |
30 | public class Request
31 | {
32 | public int Id { get; set; }
33 | public int AssigneeId { get; set; }
34 | public User Assignee { get; set; }
35 | }
36 |
37 | public class UserDTO
38 | {
39 | public int Id { get; set; }
40 | public string Name { get; set; }
41 | }
42 |
43 | public class RequestDTO
44 | {
45 | public int Id { get; set; }
46 | public UserDTO Assignee { get; set; }
47 | }
48 | ```
49 |
50 | We can write LINQ expressions against the DTO collections.
51 | ```csharp
52 | ICollection requests = await context.Request.GetItemsAsync(mapper, r => r.Id > 0 && r.Id < 3, null, new List, IIncludableQueryable>>>() { item => item.Include(s => s.Assignee) });
53 | ICollection users = await context.User.GetItemsAsync(mapper, u => u.Id > 0 && u.Id < 4, q => q.OrderBy(u => u.Name));
54 | int count = await context.Request.Query(mapper, q => q.Count(r => r.Id > 1));
55 | ```
56 | The methods below map the DTO query expresions to the equivalent data query expressions. The call to IMapper.Map converts the data query results back to the DTO (or model) object types.
57 | ```csharp
58 | static class Extensions
59 | {
60 | internal static async Task Query(this IQueryable query, IMapper mapper,
61 | Expression, TModelResult>> queryFunc) where TData : class
62 | {
63 | //Map the expressions
64 | Func, TDataResult> mappedQueryFunc = mapper.MapExpression, TDataResult>>>(queryFunc).Compile();
65 |
66 | //execute the query
67 | return mapper.Map(mappedQueryFunc(query));
68 | }
69 |
70 | internal static async Task> GetItemsAsync(this IQueryable query, IMapper mapper,
71 | Expression> filter = null,
72 | Expression, IQueryable>> queryFunc = null,
73 | ICollection, IIncludableQueryable>>> includeProperties = null)
74 | {
75 | //Map the expressions
76 | Expression> f = mapper.MapExpression>>(filter);
77 | Func, IQueryable> mappedQueryFunc = mapper.MapExpression, IQueryable>>>(queryFunc)?.Compile();
78 | ICollection, IIncludableQueryable>>> includes = mapper.MapIncludesList, IIncludableQueryable>>>(includeProperties);
79 |
80 | if (f != null)
81 | query = query.Where(f);
82 |
83 | if (includes != null)
84 | query = includes.Select(i => i.Compile()).Aggregate(query, (list, next) => query = next(query));
85 |
86 | //Call the store
87 | ICollection result = mappedQueryFunc != null ? await mappedQueryFunc(query).ToListAsync() : await query.ToListAsync();
88 |
89 | //Map and return the data
90 | return mapper.Map, IEnumerable>(result).ToList();
91 | }
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutoMapper/AutoMapper.Extensions.ExpressionMapping/b8b4900d4331d2bb42487484621b42649094fcc4/icon.png
--------------------------------------------------------------------------------
/src/AutoMapper.Extensions.ExpressionMapping/AnonymousTypeFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Reflection.Emit;
6 |
7 | namespace AutoMapper.Extensions.ExpressionMapping
8 | {
9 | internal static class AnonymousTypeFactory
10 | {
11 | private static int classCount;
12 |
13 | public static Type CreateAnonymousType(IEnumerable memberDetails)
14 | => CreateAnonymousType(memberDetails.ToDictionary(key => key.Name, element => element.GetMemberType()));
15 |
16 | public static Type CreateAnonymousType(IDictionary memberDetails)
17 | {
18 | AssemblyName dynamicAssemblyName = new AssemblyName("TempAssembly.AutoMapper.Extensions.ExpressionMapping");
19 | AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
20 | ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("TempAssembly.AutoMapper.Extensions.ExpressionMapping");
21 | TypeBuilder typeBuilder = dynamicModule.DefineType(GetAnonymousTypeName(), TypeAttributes.Public);
22 | MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
23 |
24 | var builders = memberDetails.Select
25 | (
26 | info =>
27 | {
28 | Type memberType = info.Value;
29 | string memberName = info.Key;
30 | return new
31 | {
32 | FieldBuilder = typeBuilder.DefineField(string.Concat("_", memberName), memberType, FieldAttributes.Private),
33 | PropertyBuilder = typeBuilder.DefineProperty(memberName, PropertyAttributes.HasDefault, memberType, null),
34 | GetMethodBuilder = typeBuilder.DefineMethod(string.Concat("get_", memberName), getSetAttr, memberType, Type.EmptyTypes),
35 | SetMethodBuilder = typeBuilder.DefineMethod(string.Concat("set_", memberName), getSetAttr, null, new Type[] { memberType })
36 | };
37 | }
38 | );
39 |
40 | builders.ToList().ForEach(builder =>
41 | {
42 | ILGenerator getMethodIL = builder.GetMethodBuilder.GetILGenerator();
43 | getMethodIL.Emit(OpCodes.Ldarg_0);
44 | getMethodIL.Emit(OpCodes.Ldfld, builder.FieldBuilder);
45 | getMethodIL.Emit(OpCodes.Ret);
46 |
47 | ILGenerator setMethodIL = builder.SetMethodBuilder.GetILGenerator();
48 | setMethodIL.Emit(OpCodes.Ldarg_0);
49 | setMethodIL.Emit(OpCodes.Ldarg_1);
50 | setMethodIL.Emit(OpCodes.Stfld, builder.FieldBuilder);
51 | setMethodIL.Emit(OpCodes.Ret);
52 |
53 | builder.PropertyBuilder.SetGetMethod(builder.GetMethodBuilder);
54 | builder.PropertyBuilder.SetSetMethod(builder.SetMethodBuilder);
55 | });
56 |
57 | return typeBuilder.CreateTypeInfo().AsType();
58 | }
59 |
60 | private static string GetAnonymousTypeName()
61 | => $"AnonymousType{++classCount}";
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/AutoMapper.Extensions.ExpressionMapping/AutoMapper.Extensions.ExpressionMapping.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Expression mapping (OData) extensions for AutoMapper
5 | Expression mapping (OData) extensions for AutoMapper
6 | net8.0
7 | true
8 | ..\..\AutoMapper.snk
9 | true
10 | true
11 | AutoMapper.Extensions.ExpressionMapping
12 | icon.png
13 | https://automapper.org
14 | MIT
15 | git
16 | https://github.com/AutoMapper/AutoMapper.Extensions.ExpressionMapping
17 | v
18 | true
19 | true
20 | snupkg
21 | true
22 | true
23 | true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | all
35 | runtime; build; native; contentfiles; analyzers; buildtransitive
36 |
37 |
38 |
39 |
40 |
41 |
42 | True
43 | True
44 | Resources.resx
45 |
46 |
47 |
48 |
49 |
50 | ResXFileCodeGenerator
51 | Resources.Designer.cs
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/AutoMapper.Extensions.ExpressionMapping/ConfigurationExtensions.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper.Internal;
2 | using AutoMapper.Mappers;
3 |
4 | namespace AutoMapper.Extensions.ExpressionMapping
5 | {
6 | public static class ConfigurationExtensions
7 | {
8 | public static IMapperConfigurationExpression AddExpressionMapping(this IMapperConfigurationExpression config)
9 | {
10 | config.Internal().Mappers.Insert(0, new ExpressionMapper());
11 | return config;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/AutoMapper.Extensions.ExpressionMapping/ElementTypeHelper.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper.Internal;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace AutoMapper.Extensions.ExpressionMapping
8 | {
9 | public static class ElementTypeHelper
10 | {
11 | public static Type GetElementType(Type enumerableType) => GetElementTypes(enumerableType, null)[0];
12 |
13 | public static Type[] GetElementTypes(Type enumerableType, ElementTypeFlags flags = ElementTypeFlags.None) =>
14 | GetElementTypes(enumerableType, null, flags);
15 |
16 | public static Type[] GetElementTypes(Type enumerableType, System.Collections.IEnumerable enumerable,
17 | ElementTypeFlags flags = ElementTypeFlags.None)
18 | {
19 | if (enumerableType.HasElementType)
20 | {
21 | return new[] { enumerableType.GetElementType() };
22 | }
23 |
24 | var iDictionaryType = enumerableType.GetDictionaryType();
25 | if (iDictionaryType != null && flags.HasFlag(ElementTypeFlags.BreakKeyValuePair))
26 | {
27 | return iDictionaryType.GetTypeInfo().GenericTypeArguments;
28 | }
29 |
30 | var iReadOnlyDictionaryType = enumerableType.GetReadOnlyDictionaryType();
31 | if (iReadOnlyDictionaryType != null && flags.HasFlag(ElementTypeFlags.BreakKeyValuePair))
32 | {
33 | return iReadOnlyDictionaryType.GetTypeInfo().GenericTypeArguments;
34 | }
35 |
36 | var iEnumerableType = enumerableType.GetIEnumerableType();
37 | if (iEnumerableType != null)
38 | {
39 | return iEnumerableType.GetTypeInfo().GenericTypeArguments;
40 | }
41 |
42 | if (typeof(System.Collections.IEnumerable).IsAssignableFrom(enumerableType))
43 | {
44 | var first = enumerable?.Cast