├── .editorconfig ├── .gitignore ├── LICENSE ├── NMapper.sln ├── NMapper ├── Builder │ └── NMapperConfiguration.cs ├── Extension │ └── Extension.cs ├── NMapper.csproj ├── NMapperOperator.cs ├── Strategy │ ├── AStrategy.cs │ ├── CaseStrategy.cs │ ├── CollectionStrategy.cs │ ├── DefaultNameStrategy.cs │ ├── DefaultStrategy.cs │ ├── DefaultTypeStrategy.cs │ ├── DictionaryStrategy.cs │ ├── PrimitiveTypeConverterStrategy.cs │ └── SubClassStrategy.cs └── Template │ └── NMapperTemplate.cs ├── NMapperBechmark ├── NMapperBechmark.csproj └── Program.cs ├── NMapperUT ├── NMapperUT.csproj └── UnitTest1.cs ├── README.md └── SelfTest ├── Model ├── Single │ ├── SingleDestModel.cs │ └── SingleSrcModel.cs └── SingleConvert │ ├── SingleConvertDestModel.cs │ └── SingleConvertSrcModel.cs ├── Program.cs └── SelfTest.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings: 7 | # A newline ending every file 8 | # Use 4 spaces as indentation 9 | [*] 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [project.json] 15 | indent_size = 2 16 | 17 | # C# files 18 | [*.cs] 19 | # New line preferences 20 | csharp_new_line_before_open_brace = all 21 | csharp_new_line_before_else = true 22 | csharp_new_line_before_catch = true 23 | csharp_new_line_before_finally = true 24 | csharp_new_line_before_members_in_object_initializers = true 25 | csharp_new_line_before_members_in_anonymous_types = true 26 | csharp_new_line_between_query_expression_clauses = true 27 | 28 | # Indentation preferences 29 | csharp_indent_block_contents = true 30 | csharp_indent_braces = false 31 | csharp_indent_case_contents = true 32 | csharp_indent_switch_labels = true 33 | csharp_indent_labels = one_less_than_current 34 | 35 | # avoid this. unless absolutely necessary 36 | dotnet_style_qualification_for_field = false:suggestion 37 | dotnet_style_qualification_for_property = false:suggestion 38 | dotnet_style_qualification_for_method = false:suggestion 39 | dotnet_style_qualification_for_event = false:suggestion 40 | 41 | # only use var when it's obvious what the variable type is 42 | csharp_style_var_for_built_in_types = false:none 43 | csharp_style_var_when_type_is_apparent = false:none 44 | csharp_style_var_elsewhere = false:suggestion 45 | 46 | # use language keywords instead of BCL types 47 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 48 | dotnet_style_predefined_type_for_member_access = true:suggestion 49 | 50 | # name all constant fields using PascalCase 51 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 52 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 53 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 54 | 55 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 56 | dotnet_naming_symbols.constant_fields.required_modifiers = const 57 | 58 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 59 | 60 | # static fields should have s_ prefix 61 | dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion 62 | dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields 63 | dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style 64 | 65 | dotnet_naming_symbols.static_fields.applicable_kinds = field 66 | dotnet_naming_symbols.static_fields.required_modifiers = static 67 | 68 | dotnet_naming_style.static_prefix_style.required_prefix = s_ 69 | dotnet_naming_style.static_prefix_style.capitalization = camel_case 70 | 71 | # internal and private fields should be _camelCase 72 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion 73 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields 74 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style 75 | 76 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field 77 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal 78 | 79 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 80 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 81 | 82 | # Code style defaults 83 | dotnet_sort_system_directives_first = true 84 | csharp_preserve_single_line_blocks = true 85 | csharp_preserve_single_line_statements = false 86 | 87 | # Expression-level preferences 88 | dotnet_style_object_initializer = true:suggestion 89 | dotnet_style_collection_initializer = true:suggestion 90 | dotnet_style_explicit_tuple_names = true:suggestion 91 | dotnet_style_coalesce_expression = true:suggestion 92 | dotnet_style_null_propagation = true:suggestion 93 | 94 | # Expression-bodied members 95 | csharp_style_expression_bodied_methods = false:none 96 | csharp_style_expression_bodied_constructors = false:none 97 | csharp_style_expression_bodied_operators = false:none 98 | csharp_style_expression_bodied_properties = true:none 99 | csharp_style_expression_bodied_indexers = true:none 100 | csharp_style_expression_bodied_accessors = true:none 101 | 102 | # Pattern matching 103 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 104 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 105 | csharp_style_inlined_variable_declaration = true:suggestion 106 | 107 | # Null checking preferences 108 | csharp_style_throw_expression = true:suggestion 109 | csharp_style_conditional_delegate_call = true:suggestion 110 | 111 | # Space preferences 112 | csharp_space_after_cast = false 113 | csharp_space_after_colon_in_inheritance_clause = true 114 | csharp_space_after_comma = true 115 | csharp_space_after_dot = false 116 | csharp_space_after_keywords_in_control_flow_statements = true 117 | csharp_space_after_semicolon_in_for_statement = true 118 | csharp_space_around_binary_operators = before_and_after 119 | csharp_space_around_declaration_statements = do_not_ignore 120 | csharp_space_before_colon_in_inheritance_clause = true 121 | csharp_space_before_comma = false 122 | csharp_space_before_dot = false 123 | csharp_space_before_open_square_brackets = false 124 | csharp_space_before_semicolon_in_for_statement = false 125 | csharp_space_between_empty_square_brackets = false 126 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 127 | csharp_space_between_method_call_name_and_opening_parenthesis = false 128 | csharp_space_between_method_call_parameter_list_parentheses = false 129 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 130 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 131 | csharp_space_between_method_declaration_parameter_list_parentheses = false 132 | csharp_space_between_parentheses = false 133 | csharp_space_between_square_brackets = false 134 | 135 | # C++ Files 136 | [*.{cpp,h,in}] 137 | curly_bracket_next_line = true 138 | indent_brace_style = Allman 139 | 140 | # Xml project files 141 | [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] 142 | indent_size = 2 143 | 144 | # Xml build files 145 | [*.builds] 146 | indent_size = 2 147 | 148 | # Xml files 149 | [*.{xml,stylecop,resx,ruleset}] 150 | indent_size = 2 151 | 152 | # Xml config files 153 | [*.{props,targets,config,nuspec}] 154 | indent_size = 2 155 | 156 | # Shell scripts 157 | [*.sh] 158 | end_of_line = lf 159 | [*.{cmd, bat}] 160 | end_of_line = crlf 161 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 night-moon-studio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NMapper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.452 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NMapper", "NMapper\NMapper.csproj", "{ACBECBF9-EFF8-4541-B91C-BF4347783E1F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NMapperUT", "NMapperUT\NMapperUT.csproj", "{70BF99FE-6EEA-4A42-B9B9-08FE682194FC}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NMapperBechmark", "NMapperBechmark\NMapperBechmark.csproj", "{8057AB61-7389-4929-B579-6879D9CD6606}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SelfTest", "SelfTest\SelfTest.csproj", "{88AA4A98-0AD1-47AB-9524-88D54D8D34CC}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {ACBECBF9-EFF8-4541-B91C-BF4347783E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {ACBECBF9-EFF8-4541-B91C-BF4347783E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {ACBECBF9-EFF8-4541-B91C-BF4347783E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {ACBECBF9-EFF8-4541-B91C-BF4347783E1F}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {70BF99FE-6EEA-4A42-B9B9-08FE682194FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {70BF99FE-6EEA-4A42-B9B9-08FE682194FC}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {70BF99FE-6EEA-4A42-B9B9-08FE682194FC}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {70BF99FE-6EEA-4A42-B9B9-08FE682194FC}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {8057AB61-7389-4929-B579-6879D9CD6606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {8057AB61-7389-4929-B579-6879D9CD6606}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {8057AB61-7389-4929-B579-6879D9CD6606}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {8057AB61-7389-4929-B579-6879D9CD6606}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {88AA4A98-0AD1-47AB-9524-88D54D8D34CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {88AA4A98-0AD1-47AB-9524-88D54D8D34CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {88AA4A98-0AD1-47AB-9524-88D54D8D34CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {88AA4A98-0AD1-47AB-9524-88D54D8D34CC}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {7546415F-3087-400F-A0E8-9BF320FAF26A} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /NMapper/Builder/NMapperConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using Natasha.CSharp; 3 | using NMapper.Strategy; 4 | using NMapper.Template; 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Linq; 8 | using System.Linq.Expressions; 9 | 10 | namespace NMapper 11 | { 12 | 13 | public static class NMapperConfiger 14 | { 15 | 16 | public static readonly ConcurrentBag> NameStrategies; 17 | public static readonly ConcurrentBag> TypeStrategies; 18 | 19 | static NMapperConfiger() 20 | { 21 | 22 | NameStrategies = new ConcurrentBag>(); 23 | TypeStrategies = new ConcurrentBag>(); 24 | 25 | } 26 | 27 | 28 | 29 | 30 | public static void GlobalNameStrategy(Action action) 31 | { 32 | NameStrategies.Add(action); 33 | } 34 | public static void GlobalTypeStrategy(Action action) 35 | { 36 | TypeStrategies.Add(action); 37 | } 38 | 39 | } 40 | 41 | 42 | 43 | 44 | public static class NMapperConfiger 45 | { 46 | 47 | private static readonly ConcurrentBag _configs; 48 | public static readonly AStrategy Strategy; 49 | public static readonly AStrategy DefaultNameStrategy; 50 | public static readonly AStrategy DefaultTypeStrategy; 51 | 52 | static NMapperConfiger() 53 | { 54 | 55 | _configs = new ConcurrentBag(); 56 | Strategy = new DefaultStrategy(); 57 | DefaultNameStrategy = new DefaultNameStrategy(); 58 | DefaultTypeStrategy = new DefaultTypeStrategy(); 59 | Strategy.Or(DefaultNameStrategy).And(DefaultTypeStrategy); 60 | foreach (var item in NMapperConfiger.NameStrategies) 61 | { 62 | item(DefaultNameStrategy); 63 | } 64 | foreach (var item in NMapperConfiger.TypeStrategies) 65 | { 66 | item(DefaultTypeStrategy); 67 | } 68 | 69 | 70 | NMapperConfiger.GlobalNameStrategy(strategy => 71 | { 72 | 73 | strategy.Or(); 74 | 75 | }); 76 | NMapperConfiger.GlobalTypeStrategy(strategy => 77 | { 78 | 79 | strategy 80 | .Or() 81 | .Or() 82 | .Or(); 83 | 84 | }); 85 | 86 | } 87 | 88 | 89 | 90 | 91 | ///// 92 | ///// 装载或策略 93 | ///// 94 | ///// 95 | //public static AStrategy EquipOr() where T : AStrategy, new() 96 | //{ 97 | 98 | // Strategy.Or(); 99 | // return Strategy; 100 | 101 | //} 102 | //public static AStrategy EquipOr(AStrategy strategy) 103 | //{ 104 | 105 | // Strategy.Or(strategy); 106 | // return Strategy; 107 | 108 | //} 109 | ///// 110 | ///// 装载与策略 111 | ///// 112 | ///// 113 | //public static AStrategy EquipAnd() where T : AStrategy, new() 114 | //{ 115 | 116 | // Strategy.And(); 117 | // return Strategy; 118 | 119 | //} 120 | //public static AStrategy EquipAnd(AStrategy strategy) 121 | //{ 122 | 123 | // Strategy.And(strategy); 124 | // return Strategy; 125 | 126 | //} 127 | 128 | 129 | 130 | 131 | /// 132 | /// 直接赋值一个初始化表达式,比如: var dest = new Test(); 133 | /// 134 | /// 初始化表达式的字符串 135 | internal static void SetInit(string init) 136 | { 137 | 138 | _configs.Add(() => 139 | { 140 | 141 | NMapperTemplate.SetInit(init); 142 | 143 | }); 144 | 145 | } 146 | /// 147 | /// var dest = {initFunc([script - destType])}; Func参数为目标初始化的类型。 148 | /// 149 | /// Func参数为目标初始化的类型。 150 | public static void SetInit(Func initFunc) 151 | { 152 | 153 | _configs.Add(() => 154 | { 155 | 156 | NMapperTemplate.SetInit(initFunc); 157 | 158 | }); 159 | 160 | } 161 | 162 | 163 | 164 | 165 | /// 166 | /// 在映射时忽略某个成员 167 | /// 168 | /// 目标成员名 169 | public static void Ignore(string destMember) 170 | { 171 | 172 | _configs.Add(() => 173 | { 174 | 175 | NMapperTemplate.DirectMapping(destMember, default); 176 | NMapperTemplate.AssignmentMapping(destMember, default); 177 | 178 | }); 179 | 180 | } 181 | public static void Ignore(Expression> expression) 182 | { 183 | 184 | var body = expression.Body.ToString(); 185 | Ignore(body.Split('.')[1]); 186 | 187 | } 188 | 189 | 190 | 191 | 192 | /// 193 | /// 将目标成员设置为初始值 194 | /// 195 | /// 目标成员名 196 | public static void Default(string destMember) 197 | { 198 | 199 | _configs.Add(() => 200 | { 201 | 202 | NMapperTemplate.AssignmentMapping(destMember, "default"); 203 | NMapperTemplate.DirectMapping(destMember, default); 204 | 205 | }); 206 | 207 | } 208 | public static void Default(Expression> expression) 209 | { 210 | 211 | var body = expression.Body.ToString(); 212 | Default(body.Split('.')[1]); 213 | 214 | } 215 | 216 | 217 | 218 | 219 | /// 220 | /// dest.{destMember} = {srcScript}; 221 | /// 222 | /// 目标成员 223 | /// 赋值表达式 224 | public static void AssignmentMapping(string destMember, string srcScript) 225 | { 226 | 227 | _configs.Add(() => 228 | { 229 | 230 | NMapperTemplate.AssignmentMapping(destMember, srcScript); 231 | 232 | }); 233 | 234 | } 235 | public static void AsseignmentMapping(Expression> destMemberExpression, string srcScript) 236 | { 237 | 238 | var body = destMemberExpression.Body.ToString(); 239 | AssignmentMapping(body.Split('.')[1], srcScript); 240 | 241 | } 242 | public static void AsseignmentMapping(string destMember, Expression> srcScriptExpression) 243 | { 244 | 245 | var body = srcScriptExpression.Body.ToString(); 246 | AssignmentMapping(destMember, body.Split('.')[1]); 247 | 248 | } 249 | public static void AsseignmentMapping(Expression> destMemberExpression, Expression> srcScriptExpression) 250 | { 251 | 252 | var destBody = destMemberExpression.Body.ToString(); 253 | var srcBody = srcScriptExpression.Body.ToString(); 254 | AssignmentMapping(destBody.Split('.')[1], srcBody.Split('.')[1]); 255 | 256 | } 257 | 258 | 259 | 260 | 261 | /// 262 | /// {destScript}; 263 | /// 264 | /// 目标成员 265 | /// 段代码 266 | public static void DirectMapping(string destMember, string destScript) 267 | { 268 | 269 | _configs.Add(() => 270 | { 271 | 272 | NMapperTemplate.DirectMapping(destMember, destScript); 273 | 274 | }); 275 | 276 | } 277 | public static void DirectMapping(Expression> destMemberExpression, string srcScript) 278 | { 279 | 280 | var body = destMemberExpression.Body.ToString(); 281 | DirectMapping(body.Split('.')[1], srcScript); 282 | 283 | } 284 | 285 | 286 | 287 | 288 | private static void Handler() 289 | { 290 | 291 | var srcMembers = NBuildInfo.GetInfos(); 292 | var destMembers = NBuildInfo.GetInfos(); 293 | var destList = destMembers.Values.Where(item => item != null && item.CanRead); 294 | foreach (var itemDest in destList) 295 | { 296 | 297 | foreach (var item in srcMembers) 298 | { 299 | 300 | var itemSrc = item.Value; 301 | if (itemSrc != null && itemSrc.CanWrite) 302 | { 303 | 304 | (ScriptConnectionType Type, string Script) result = default; 305 | if (Strategy.Check(itemSrc, itemDest, ref result)) 306 | { 307 | 308 | if (result != default) 309 | { 310 | 311 | if (result.Type == ScriptConnectionType.NoAssignment) 312 | { 313 | NMapperTemplate.DirectMapping(itemDest.MemberName, result.Script); 314 | } 315 | else if (result.Type == ScriptConnectionType.NeedAssignment) 316 | { 317 | NMapperTemplate.AssignmentMapping(itemDest.MemberName, result.Script); 318 | } 319 | 320 | } 321 | 322 | } 323 | 324 | } 325 | 326 | } 327 | 328 | } 329 | foreach (var item in _configs) 330 | { 331 | item(); 332 | } 333 | 334 | } 335 | 336 | 337 | 338 | 339 | public static Func Complie() 340 | { 341 | 342 | Handler(); 343 | var func = NDelegate.RandomDomain().Func(NMapperTemplate.Script, typeof(TSrc), typeof(TDest)); 344 | MapperOperator.Map = func; 345 | return func; 346 | 347 | } 348 | 349 | } 350 | 351 | } 352 | -------------------------------------------------------------------------------- /NMapper/Extension/Extension.cs: -------------------------------------------------------------------------------- 1 | namespace NMapper 2 | { 3 | public static class NMapperExtension 4 | { 5 | 6 | public static TDest Map(this TSrc src) 7 | { 8 | return MapperOperator.MapFrom(src); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /NMapper/NMapper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | TRACE 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /NMapper/NMapperOperator.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using Natasha.CSharp; 3 | using System; 4 | using System.Collections.Concurrent; 5 | 6 | namespace NMapper 7 | { 8 | public static class MapperOperator 9 | { 10 | 11 | public static TDest MapFrom(TSrc src) 12 | { 13 | 14 | Type srcType = src.GetType(); 15 | if (srcType == MapperOperator._srcType) 16 | { 17 | 18 | return MapperOperator.Map(src); 19 | 20 | } 21 | else 22 | { 23 | 24 | return MapperOperator.SubClassMap(src, srcType.GetDevelopName()); 25 | 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | 33 | public static class MapperOperator 34 | { 35 | 36 | public static Func Map; 37 | internal static readonly Type _srcType; 38 | private static readonly string _destScript; 39 | private static readonly ConcurrentDictionary> _cache; 40 | private static PrecisionCache> _precisionCache; 41 | 42 | 43 | static MapperOperator() 44 | { 45 | _srcType = typeof(TSrc); 46 | _destScript = typeof(TDest).GetDevelopName(); 47 | _cache = new ConcurrentDictionary>(); 48 | _precisionCache = _cache.PrecisioTree(); 49 | Complie(); 50 | } 51 | 52 | 53 | 54 | 55 | internal static TDest SubClassMap(TSrc src,string realType) 56 | { 57 | 58 | var func = _precisionCache.GetValue(realType); 59 | if (func!=default) 60 | { 61 | 62 | return func(src); 63 | 64 | } 65 | else 66 | { 67 | 68 | var result = NDelegate.RandomDomain().Func($"return MapperOperator<{realType}, {_destScript}>.Handler(({realType})arg);"); 69 | _cache[realType] = result; 70 | _precisionCache = _cache.PrecisioTree(); 71 | return result(src); 72 | 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | public static void Complie() 80 | { 81 | Map = NMapperConfiger.Complie(); 82 | } 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /NMapper/Strategy/AStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using NMapper.Strategy; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | 7 | namespace NMapper 8 | { 9 | 10 | public abstract class AStrategy 11 | { 12 | 13 | private readonly LinkedList _or_strategies; 14 | private readonly LinkedList _and_strategies; 15 | 16 | 17 | public AStrategy() 18 | { 19 | 20 | _or_strategies = new LinkedList(); 21 | _and_strategies = new LinkedList(); 22 | 23 | } 24 | 25 | 26 | 27 | 28 | /// 29 | /// 增加或条件 30 | /// 31 | /// 策略 32 | /// 33 | public AStrategy Or() where TStrategy : AStrategy, new() 34 | { 35 | 36 | return Or(new TStrategy()); 37 | 38 | } 39 | public AStrategy Or(AStrategy strategy) 40 | { 41 | 42 | lock (_or_strategies) 43 | { 44 | _or_strategies.AddLast(strategy); 45 | } 46 | return this; 47 | 48 | } 49 | 50 | 51 | 52 | 53 | /// 54 | /// 增加与条件 55 | /// 56 | /// 策略 57 | /// 58 | public AStrategy And() where TStrategy : AStrategy, new() 59 | { 60 | 61 | return And(new TStrategy()); 62 | 63 | } 64 | public AStrategy And(AStrategy strategy) 65 | { 66 | 67 | lock (_and_strategies) 68 | { 69 | _and_strategies.AddLast(strategy); 70 | } 71 | return this; 72 | 73 | } 74 | 75 | 76 | 77 | 78 | /// 79 | /// 移除或策略 80 | /// 81 | /// 82 | /// 83 | public AStrategy RemoveOr(AStrategy strategy) 84 | { 85 | 86 | return RemoveOr(strategy.GetType()); 87 | 88 | } 89 | public AStrategy RemoveOr() where TStrategy : AStrategy, new() 90 | { 91 | 92 | return RemoveOr(typeof(TStrategy)); 93 | 94 | } 95 | public AStrategy RemoveOr(Type strategyType) 96 | { 97 | 98 | lock (_or_strategies) 99 | { 100 | 101 | var temp = _or_strategies.First; 102 | for (int i = 0; i < _or_strategies.Count; i += 1) 103 | { 104 | 105 | if (temp.Value.GetType() == strategyType) 106 | { 107 | _or_strategies.Remove(temp); 108 | return this; 109 | } 110 | temp = temp.Next; 111 | 112 | } 113 | 114 | } 115 | return this; 116 | 117 | } 118 | 119 | 120 | /// 121 | /// 移除与策略 122 | /// 123 | /// 124 | /// 125 | public AStrategy RemoveAnd(AStrategy strategy) 126 | { 127 | 128 | return RemoveAnd(strategy.GetType()); 129 | 130 | } 131 | public AStrategy RemoveAnd() where TStrategy : AStrategy, new() 132 | { 133 | 134 | return RemoveAnd(typeof(TStrategy)); 135 | 136 | } 137 | public AStrategy RemoveAnd(Type strategyType) 138 | { 139 | 140 | lock (_and_strategies) 141 | { 142 | 143 | var temp = _and_strategies.First; 144 | for (int i = 0; i < _or_strategies.Count; i += 1) 145 | { 146 | 147 | if (temp.Value.GetType() == strategyType) 148 | { 149 | _and_strategies.Remove(temp); 150 | return this; 151 | } 152 | temp = temp.Next; 153 | 154 | } 155 | 156 | } 157 | return this; 158 | 159 | } 160 | 161 | 162 | 163 | 164 | /// 165 | /// 检测条件 166 | /// 167 | /// 源对象属性信息 168 | /// 生成对象的属性信息 169 | /// 170 | public virtual bool Condition(NBuildInfo srcMember, NBuildInfo destMember) { return false; } 171 | /// 172 | /// 生成脚本 173 | /// 174 | /// 源对象属性信息 175 | /// 生成对象的属性信息 176 | /// 177 | public virtual (ScriptConnectionType Type, string Script) Handler(NBuildInfo srcMember, NBuildInfo destMember) 178 | { 179 | return default; 180 | } 181 | 182 | 183 | 184 | 185 | /// 186 | /// 对成员进行策略检查 187 | /// 188 | /// 189 | /// 190 | /// 191 | internal bool Check(NBuildInfo srcMember, NBuildInfo destMember, ref (ScriptConnectionType Type, string Script) handler) 192 | { 193 | 194 | //当前策略判断 195 | bool shut = Condition(srcMember, destMember); 196 | 197 | 198 | //或策略链判断 199 | lock (_or_strategies) 200 | { 201 | 202 | foreach (var item in _or_strategies) 203 | { 204 | 205 | shut = shut || item.Check(srcMember, destMember, ref handler); 206 | 207 | } 208 | 209 | } 210 | 211 | 212 | if (shut) 213 | { 214 | 215 | //与策略链判断 216 | lock (_and_strategies) 217 | { 218 | 219 | foreach (var item in _and_strategies) 220 | { 221 | 222 | shut = shut && item.Check(srcMember, destMember, ref handler); 223 | 224 | } 225 | 226 | 227 | } 228 | 229 | } 230 | 231 | 232 | //如果当前与子链策略全通过了,则去获取结果 233 | if (shut) 234 | { 235 | 236 | if (handler == default) 237 | { 238 | handler = Handler(srcMember, destMember); 239 | } 240 | 241 | } 242 | 243 | return shut; 244 | 245 | } 246 | 247 | 248 | 249 | 250 | public static AStrategy operator |(AStrategy pre, AStrategy after) 251 | { 252 | return pre.Or(after); 253 | } 254 | public static AStrategy operator &(AStrategy pre, AStrategy after) 255 | { 256 | return pre.And(after); 257 | } 258 | 259 | } 260 | 261 | 262 | 263 | 264 | public enum ScriptConnectionType 265 | { 266 | 267 | None, 268 | /// 269 | /// src.Member = {You need write the Script by yourself}; 270 | /// 271 | NeedAssignment, 272 | /// 273 | /// {You need write the all code by yourself}; 274 | /// 275 | NoAssignment 276 | 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /NMapper/Strategy/CaseStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | 3 | namespace NMapper.Strategy 4 | { 5 | 6 | public class CaseStrategy : AStrategy 7 | { 8 | 9 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 10 | { 11 | return srcMember.MemberName.ToLower() == destMember.MemberName.ToLower(); 12 | } 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /NMapper/Strategy/CollectionStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace NMapper.Strategy 9 | { 10 | public class CollectionStrategy : AStrategy 11 | { 12 | 13 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 14 | { 15 | 16 | return (!srcMember.MemberType.IsImplementFrom(destMember.MemberType)) && 17 | ( 18 | 19 | destMember.MemberType != srcMember.MemberType 20 | 21 | ) && ( 22 | 23 | destMember.MemberType.IsImplementFrom(typeof(IEnumerable)) && 24 | !destMember.MemberType.IsImplementFrom(typeof(IDictionary)) 25 | ) && ( 26 | 27 | srcMember.MemberType.IsImplementFrom(typeof(IEnumerable)) && 28 | !srcMember.MemberType.IsImplementFrom(typeof(IDictionary)) 29 | 30 | ); 31 | 32 | } 33 | 34 | 35 | 36 | 37 | public override (ScriptConnectionType Type, string Script) Handler(NBuildInfo srcMember, NBuildInfo destMember) 38 | { 39 | 40 | return (ScriptConnectionType.NoAssignment, CollectionScript(srcMember.MemberType,$"arg.{srcMember.MemberName}",destMember.MemberType, $"dest.{destMember.MemberName}")); 41 | 42 | } 43 | 44 | 45 | 46 | public static string CollectionScript(Type srcType, string srcMember, Type destType, string destMember) 47 | { 48 | 49 | StringBuilder builder = new StringBuilder(); 50 | builder.AppendLine($@"if({srcMember} != default){{"); 51 | 52 | 53 | //如果目标成员是泛型集合 54 | if (destType.IsGenericType) 55 | { 56 | 57 | builder.AppendLine($"{destMember} =" + CollectionToCollectionScript(srcType, destType, $@"{srcMember}")); 58 | 59 | } 60 | else 61 | { 62 | 63 | builder.AppendLine($"{destMember} =" + CollectionToArrayScript(srcType, destType, $@"{srcMember}")); 64 | 65 | } 66 | builder.Append('}'); 67 | 68 | return builder.ToString(); 69 | } 70 | 71 | 72 | 73 | 74 | public static string CollectionToCollectionScript(Type srcType, Type destType, string srcMember) 75 | { 76 | 77 | //获取集合元素的类型 78 | Type srcEleType = default; 79 | if (srcType.IsGenericType) 80 | { 81 | srcEleType = srcType.GetGenericArguments()[0]; 82 | } 83 | else if (srcType.IsArray) 84 | { 85 | srcEleType = srcType.GetElementType(); 86 | } 87 | 88 | 89 | //如果目标成员是泛型集合 90 | var destEleType = destType.GetGenericArguments()[0]; 91 | 92 | //元素类型相同 93 | if (destEleType == srcEleType) 94 | { 95 | 96 | if (destType.IsInterface) 97 | { 98 | 99 | return $@"{srcMember};"; 100 | 101 | } 102 | else 103 | { 104 | 105 | return $@"new {destType.GetDevelopName()}({srcMember});"; 106 | 107 | } 108 | 109 | } 110 | //元素类型可以相互转换 111 | else if ( 112 | (destEleType.IsPrimitive || destEleType == typeof(string)) && 113 | (srcEleType.IsPrimitive || srcEleType == typeof(string)) 114 | ) 115 | { 116 | 117 | if (destType.IsInterface) 118 | { 119 | 120 | return $@"{srcMember}.Select(item=>Convert.To{destEleType.Name}(item));"; 121 | 122 | } 123 | else 124 | { 125 | 126 | return $@"new {destType.GetDevelopName()}({srcMember}.Select(item=>Convert.To{destEleType.Name}(item)));"; 127 | 128 | } 129 | 130 | } 131 | //源继承了目标类型 132 | else if (srcEleType.IsImplementFrom(destEleType)) 133 | { 134 | 135 | if (destType.IsInterface) 136 | { 137 | 138 | return $@"{srcMember};"; 139 | 140 | } 141 | else 142 | { 143 | 144 | return $@"new {destType.GetDevelopName()}({srcMember});"; 145 | 146 | } 147 | 148 | } 149 | //其他 150 | else 151 | { 152 | 153 | if (destType.IsInterface) 154 | { 155 | 156 | return $@"{srcMember}.Select(item=>MapperOperator<{destEleType.GetDevelopName()}>.MapFrom(item));"; 157 | 158 | } 159 | else 160 | { 161 | 162 | return $@"new {destType.GetDevelopName()}({srcMember}.Select(item=>MapperOperator<{destEleType.GetDevelopName()}>.MapFrom(item)));"; 163 | 164 | } 165 | 166 | } 167 | 168 | 169 | } 170 | 171 | 172 | 173 | 174 | public static string CollectionToArrayScript(Type srcType, Type destType, string srcMember) 175 | { 176 | 177 | //获取集合元素的类型 178 | Type srcEleType = default; 179 | if (srcType.IsGenericType) 180 | { 181 | srcEleType = srcType.GetGenericArguments()[0]; 182 | } 183 | else if (srcType.IsArray) 184 | { 185 | srcEleType = srcType.GetElementType(); 186 | } 187 | 188 | 189 | string script = default; 190 | var destEleType = destType.GetElementType(); 191 | if (destEleType == srcEleType) 192 | { 193 | 194 | script = $@"{srcMember}.ToArray();"; 195 | 196 | } 197 | else if (srcEleType.IsImplementFrom(destEleType)) 198 | { 199 | 200 | if (srcType.IsArray) 201 | { 202 | 203 | script = $@"{srcMember};"; 204 | 205 | } 206 | else 207 | { 208 | script = $@"{srcMember}.Select(item=>MapperOperator<{destEleType.GetDevelopName()}>.MapFrom(item)).ToArray();"; 209 | } 210 | 211 | } 212 | else if ( 213 | (destEleType.IsPrimitive || destEleType == typeof(string)) && 214 | (srcEleType.IsPrimitive || srcEleType == typeof(string)) 215 | ) 216 | { 217 | 218 | script = $@"{srcMember}.Select(item=>Convert.To{destEleType.Name}(item)).ToArray();"; 219 | 220 | } 221 | else 222 | { 223 | 224 | script = $@"{srcMember}.Select(item=>MapperOperator<{destEleType.GetDevelopName()}>.MapFrom(item)).ToArray();"; 225 | 226 | } 227 | return script; 228 | 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /NMapper/Strategy/DefaultNameStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | 3 | namespace NMapper.Strategy 4 | { 5 | 6 | public class DefaultNameStrategy : AStrategy 7 | { 8 | 9 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 10 | { 11 | 12 | return srcMember.MemberName == destMember.MemberName; 13 | 14 | } 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /NMapper/Strategy/DefaultStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using System; 3 | 4 | namespace NMapper 5 | { 6 | public class DefaultStrategy : AStrategy 7 | { 8 | 9 | public override (ScriptConnectionType Type, string Script) Handler(NBuildInfo srcMember, NBuildInfo destMember) 10 | { 11 | return (ScriptConnectionType.NeedAssignment, $"arg.{srcMember.MemberName}"); 12 | } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /NMapper/Strategy/DefaultTypeStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | 3 | namespace NMapper.Strategy 4 | { 5 | public class DefaultTypeStrategy : AStrategy 6 | { 7 | 8 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 9 | { 10 | 11 | return srcMember.MemberType == destMember.MemberType; 12 | 13 | } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /NMapper/Strategy/DictionaryStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Linq; 7 | using System.Collections.Concurrent; 8 | 9 | namespace NMapper.Strategy 10 | { 11 | public class DictionaryStrategy : AStrategy 12 | { 13 | 14 | public readonly ConcurrentDictionary MappingCache; 15 | public DictionaryStrategy():base() 16 | { 17 | MappingCache = new ConcurrentDictionary(); 18 | } 19 | 20 | 21 | 22 | 23 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 24 | { 25 | return (!srcMember.MemberType.IsImplementFrom(destMember.MemberType)) && 26 | destMember.MemberType != srcMember.MemberType && 27 | srcMember.MemberType.IsImplementFrom(typeof(IDictionary<,>)) && 28 | destMember.MemberType.IsImplementFrom(); 29 | } 30 | 31 | 32 | 33 | 34 | public override (ScriptConnectionType Type, string Script) Handler(NBuildInfo srcMember, NBuildInfo destMember) 35 | { 36 | 37 | var srcMemberScript = $"arg.{srcMember.MemberName}"; 38 | var destMemberScrtpt = $"dest.{destMember.MemberName}"; 39 | 40 | 41 | var destCacheKey = $"-dict-{destMember.MemberName}-key"; 42 | var destCacheValue = $"-dict-{destMember.MemberName}-value"; 43 | 44 | 45 | StringBuilder builder = new StringBuilder(); 46 | builder.AppendLine($@"if({srcMemberScript} != default){{"); 47 | if (destMember.MemberType.IsImplementFrom()) 48 | { 49 | 50 | if (destMember.MemberType.IsInterface) 51 | { 52 | 53 | var destTypes = destMember.MemberType.GetGenericArguments(); 54 | var destKeyType = destTypes[0]; 55 | var destValueType = destTypes[1]; 56 | if (srcMember.MemberType == typeof(IDictionary)) 57 | { 58 | 59 | var tempVarName = $"@temp_dict_{destMember.MemberName}"; 60 | if (!destMember.MemberType.IsInterface) 61 | { 62 | 63 | //IDictionary -> Dictionary 64 | builder.AppendLine($@"var {tempVarName}= {srcMemberScript}.Keys; 65 | {destMemberScrtpt} = new {destMember.MemberTypeName}(); 66 | foreach(var item in {tempVarName}){{"); 67 | var key = MappingCache.ContainsKey(destCacheKey) ? default : $"({ destKeyType.GetDevelopName()})item"; 68 | var value = MappingCache.ContainsKey(destCacheValue) ? default : $"({destValueType.GetDevelopName()})({srcMemberScript}[item])"; 69 | builder.AppendLine($@"{destMemberScrtpt}[{key}] = {value};"); 70 | builder.AppendLine("}"); 71 | 72 | } 73 | else 74 | { 75 | 76 | //IDictionary -> IDictionary 77 | builder.AppendLine($@"var {tempVarName} = {srcMemberScript}.Keys; 78 | {destMemberScrtpt} = new Dictionary<{destKeyType.GetDevelopName()},{destValueType.GetDevelopName()}>(); 79 | foreach(var item in {tempVarName}){{"); 80 | var key = MappingCache.ContainsKey(destCacheKey) ? default : $"({ destKeyType.GetDevelopName()})item"; 81 | var value = MappingCache.ContainsKey(destCacheValue) ? default : $"({destValueType.GetDevelopName()})({srcMemberScript}[item])"; 82 | builder.AppendLine($@"{destMemberScrtpt}[{key}] = {value};"); 83 | builder.AppendLine("}"); 84 | 85 | } 86 | 87 | } 88 | else 89 | { 90 | 91 | //IDictionary -> IDictionary 92 | builder.Append(destMemberScrtpt + " = "); 93 | if (!destMember.MemberType.IsInterface) 94 | { 95 | builder.Append($"new {destMember.MemberTypeName}"); 96 | } 97 | builder.Append($"({srcMemberScript}.Select(item=>KeyValuePair.Create("); 98 | 99 | 100 | var srcTypes = srcMember.MemberType.GetGenericArguments(); 101 | var srcKeyType = srcTypes[0]; 102 | var srctValueType = srcTypes[1]; 103 | 104 | 105 | if (destKeyType != srcKeyType) 106 | { 107 | 108 | if (MappingCache.ContainsKey(destCacheKey)) 109 | { 110 | 111 | builder.Append(MappingCache[destCacheKey]); 112 | 113 | } 114 | else if ( 115 | (destKeyType.IsPrimitive || destKeyType == typeof(string)) && 116 | (srcKeyType.IsPrimitive || srcKeyType == typeof(string))) 117 | { 118 | 119 | builder.Append($"Convert.To{destKeyType.Name}(item.Key)"); 120 | 121 | } 122 | else 123 | { 124 | 125 | builder.Append($"MapperOperator<{destKeyType.GetDevelopName()}>.MapFrom(item.Key)"); 126 | 127 | } 128 | 129 | } 130 | else 131 | { 132 | 133 | builder.Append($"item.Key"); 134 | 135 | } 136 | 137 | 138 | if (destValueType != srctValueType) 139 | { 140 | 141 | if (MappingCache.ContainsKey(destCacheValue)) 142 | { 143 | 144 | // key = Key.Name 145 | builder.Append(MappingCache[destCacheValue]); 146 | 147 | } 148 | else if ( 149 | (destValueType.IsPrimitive || destValueType == typeof(string)) && 150 | (srctValueType.IsPrimitive || srctValueType == typeof(string))) 151 | { 152 | 153 | // key = Convert.Int32(Key) 154 | builder.Append($"Convert.To{destValueType.Name}(item.Value)"); 155 | 156 | } 157 | else 158 | { 159 | 160 | // Key = MapperOperator.MapFrom(Key); 161 | builder.Append($"MapperOperator<{destValueType.GetDevelopName()}>.MapFrom(item.Value)"); 162 | 163 | } 164 | 165 | } 166 | else 167 | { 168 | 169 | builder.Append($"item.Value"); 170 | 171 | } 172 | 173 | 174 | builder.Append(')'); 175 | } 176 | 177 | } 178 | 179 | } 180 | else 181 | { 182 | 183 | var destTypes = destMember.MemberType.GetGenericArguments(); 184 | var destKeyType = destTypes[0]; 185 | var destValueType = destTypes[1]; 186 | 187 | if (destMember.MemberType.IsInterface) 188 | { 189 | 190 | if (srcMember.MemberType == typeof(IDictionary) || 191 | (destMember.MemberType == typeof(IEnumerable) || 192 | destMember.MemberType == typeof(ICollection))) 193 | { 194 | 195 | //IDictionary -> ICollection / IEnumable 196 | if (MappingCache.ContainsKey(destCacheKey)) 197 | { 198 | 199 | var tempScript = MappingCache[destCacheKey]; 200 | if (tempScript == default) 201 | { 202 | 203 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Keys;"); 204 | 205 | } 206 | else 207 | { 208 | 209 | builder.Append($"{destMemberScrtpt} = {tempScript};"); 210 | 211 | } 212 | 213 | } 214 | else if (MappingCache.ContainsKey(destCacheValue)) 215 | { 216 | 217 | var tempScript = MappingCache[destCacheValue]; 218 | if (tempScript == default) 219 | { 220 | 221 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Values;"); 222 | 223 | } 224 | else 225 | { 226 | 227 | builder.Append($"{destMemberScrtpt} = {tempScript};"); 228 | 229 | } 230 | 231 | } 232 | else 233 | { 234 | 235 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Values;"); 236 | 237 | } 238 | 239 | } 240 | else 241 | { 242 | 243 | //IDictionary -> ICollection / IEnumable 244 | if (MappingCache.ContainsKey(destCacheKey)) 245 | { 246 | 247 | var tempScript = MappingCache[destCacheKey]; 248 | if (tempScript == default) 249 | { 250 | 251 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Keys;"); 252 | 253 | } 254 | else 255 | { 256 | 257 | builder.Append($"{destMemberScrtpt} = {tempScript};"); 258 | 259 | } 260 | 261 | } 262 | else if (MappingCache.ContainsKey(destCacheValue)) 263 | { 264 | 265 | var tempScript = MappingCache[destCacheValue]; 266 | if (tempScript == default) 267 | { 268 | 269 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Values;"); 270 | 271 | } 272 | else 273 | { 274 | 275 | builder.Append($"{destMemberScrtpt} = {tempScript};"); 276 | 277 | } 278 | 279 | } 280 | else 281 | { 282 | 283 | builder.Append($"{destMemberScrtpt} = {srcMemberScript}.Values;"); 284 | 285 | } 286 | 287 | } 288 | 289 | } 290 | 291 | 292 | //Dict -> ICollection 293 | 294 | if (!destMember.MemberType.IsInterface) 295 | { 296 | 297 | if (MappingCache.ContainsKey(destCacheKey)) 298 | { 299 | } 300 | } 301 | 302 | else if (MappingCache.ContainsKey(destCacheValue)) 303 | { 304 | 305 | } 306 | //IDictionary -> Dictionary 307 | builder.AppendLine($@"var temp{destMember.MemberName} = {srcMemberScript}.Keys; 308 | dest.{destMember.MemberName} = new {destMember.MemberTypeName}(); 309 | foreach(var item in temp{destMember.MemberName}){{"); 310 | var key = MappingCache.ContainsKey($"-dict-{destMember.MemberName}-key") ? default : $"({ destKeyType.GetDevelopName()})item"; 311 | var value = MappingCache.ContainsKey($"-dict-{destMember.MemberName}-value") ? default : $"({destValueType.GetDevelopName()})({srcMemberScript}[item])"; 312 | builder.AppendLine($@"dest.{destMember.MemberName}[{key}] = {value};"); 313 | builder.AppendLine("}"); 314 | 315 | } 316 | else 317 | { 318 | 319 | //IDictionary -> IDictionary 320 | builder.AppendLine($@" var temp{destMember.MemberName} = {srcMemberScript}.Keys; 321 | var tempDict{destMember.MemberName} = new Dictionary<{destKeyType.GetDevelopName()},{destValueType.GetDevelopName()}>(); 322 | foreach(var item in temp{destMember.MemberName}){{"); 323 | var key = MappingCache.ContainsKey($"-dict-{destMember.MemberName}-key") ? default : $"({ destKeyType.GetDevelopName()})item"; 324 | var value = MappingCache.ContainsKey($"-dict-{destMember.MemberName}-value") ? default : $"({destValueType.GetDevelopName()})({srcMemberScript}[item])"; 325 | builder.AppendLine($@"tempDict[{key}] = {value};"); 326 | builder.AppendLine("}"); 327 | builder.AppendLine($"dest.{destMember.MemberName} = tempDict{destMember.MemberName};"); 328 | 329 | } 330 | 331 | else 332 | { 333 | 334 | //IDictionary -> IDictionary 335 | if (!destMember.MemberType.IsInterface) 336 | { 337 | builder.Append($"new {destMember.MemberTypeName}"); 338 | } 339 | builder.Append($"({srcMemberScript}.Select(item=>KeyValuePair.Create("); 340 | 341 | 342 | var srcTypes = srcMember.MemberType.GetGenericArguments(); 343 | var srcKeyType = srcTypes[0]; 344 | var srctValueType = srcTypes[1]; 345 | 346 | 347 | if (destKeyType != srcKeyType) 348 | { 349 | 350 | var tempKey = $"-dict-{destMember.MemberName}-key"; 351 | if (MappingCache.ContainsKey(tempKey)) 352 | { 353 | 354 | builder.Append(MappingCache[tempKey]); 355 | 356 | } 357 | else if ( 358 | (destKeyType.IsPrimitive || destKeyType == typeof(string)) && 359 | (srcKeyType.IsPrimitive || srcKeyType == typeof(string))) 360 | { 361 | 362 | builder.Append($"Convert.To{destKeyType.Name}(item.Key)"); 363 | 364 | } 365 | else 366 | { 367 | 368 | builder.Append($"MapperOperator<{destKeyType.GetDevelopName()}>.MapFrom(item.Key)"); 369 | 370 | } 371 | 372 | } 373 | else 374 | { 375 | 376 | builder.Append($"item.Key"); 377 | 378 | } 379 | 380 | 381 | if (destValueType != srctValueType) 382 | { 383 | 384 | 385 | var tempKey = $"-dict-{destMember.MemberName}-value"; 386 | if (MappingCache.ContainsKey(tempKey)) 387 | { 388 | 389 | // key = Key.Name 390 | builder.Append(MappingCache[tempKey]); 391 | 392 | } 393 | else if ( 394 | (destValueType.IsPrimitive || destValueType == typeof(string)) && 395 | (srctValueType.IsPrimitive || srctValueType == typeof(string))) 396 | { 397 | 398 | // key = Convert.Int32(Key) 399 | builder.Append($"Convert.To{destValueType.Name}(item.Value)"); 400 | 401 | } 402 | else 403 | { 404 | 405 | // Key = MapperOperator.MapFrom(Key); 406 | builder.Append($"MapperOperator<{destValueType.GetDevelopName()}>.MapFrom(item.Value)"); 407 | 408 | } 409 | 410 | } 411 | else 412 | { 413 | 414 | builder.Append($"item.Value"); 415 | 416 | } 417 | 418 | 419 | builder.Append(')'); 420 | } 421 | 422 | return base.Handler(srcMember, destMember); 423 | 424 | } 425 | 426 | 427 | 428 | 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /NMapper/Strategy/PrimitiveTypeConverterStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace NMapper.Strategy 7 | { 8 | public class PrimitiveTypeConverterStrategy : AStrategy 9 | { 10 | 11 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 12 | { 13 | 14 | return (srcMember.MemberType != destMember.MemberType && 15 | (srcMember.MemberType.IsPrimitive || srcMember.MemberType == typeof(string)) && 16 | (destMember.MemberType.IsPrimitive || destMember.MemberType == typeof(string))); 17 | 18 | } 19 | 20 | public override (ScriptConnectionType Type, string Script) Handler(NBuildInfo srcMember, NBuildInfo destMember) 21 | { 22 | return (ScriptConnectionType.NeedAssignment, $"Convert.To{destMember.MemberType.Name}(arg.{srcMember.MemberName})"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /NMapper/Strategy/SubClassStrategy.cs: -------------------------------------------------------------------------------- 1 | using Natasha; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace NMapper.Strategy 7 | { 8 | public class SubClassStrategy:AStrategy 9 | { 10 | public override bool Condition(NBuildInfo srcMember, NBuildInfo destMember) 11 | { 12 | return srcMember.MemberType.IsImplementFrom(destMember.MemberType) || srcMember.MemberType.IsSubclassOf(destMember.MemberType); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /NMapper/Template/NMapperTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Text; 4 | 5 | namespace NMapper.Template 6 | { 7 | public static class NMapperTemplate 8 | { 9 | 10 | private static string _init; 11 | private static readonly ConcurrentDictionary _needAssignmentMemberMappings; 12 | private static readonly ConcurrentDictionary _noAssignmentMemberMappings; 13 | static NMapperTemplate() 14 | { 15 | 16 | SetInit(item => $"new {item}();"); 17 | _needAssignmentMemberMappings = new ConcurrentDictionary(); 18 | _noAssignmentMemberMappings = new ConcurrentDictionary(); 19 | 20 | } 21 | 22 | 23 | 24 | 25 | /// 26 | /// 直接赋值一个初始化表达式,比如: var dest = new Test(); 27 | /// 28 | /// 初始化表达式的字符串 29 | internal static void SetInit(string init) 30 | { 31 | 32 | _init = init; 33 | 34 | } 35 | /// 36 | /// var dest = {initFunc([script - destType])}; Func参数为目标初始化的类型。 37 | /// 38 | /// Func参数为目标初始化的类型。 39 | internal static void SetInit(Func initFunc) 40 | { 41 | 42 | _init = "var dest = " + initFunc(typeof(TDest).GetDevelopName()); 43 | StringBuilder builder = new StringBuilder(); 44 | if (!typeof(TSrc).IsValueType && !(typeof(TSrc) == typeof(string))) 45 | { 46 | builder.AppendLine(@"if(arg == default){return dest;}"); 47 | } 48 | 49 | } 50 | 51 | 52 | 53 | 54 | /// 55 | /// dest.{destMember} = {srcScript}; 56 | /// 57 | /// 目标成员 58 | /// 赋值表达式 59 | internal static void AssignmentMapping(string destMember, string srcScript) 60 | { 61 | 62 | _needAssignmentMemberMappings[destMember] = srcScript; 63 | 64 | } 65 | /// 66 | /// {destScript}; 67 | /// 68 | /// 目标成员 69 | /// 段代码 70 | internal static void DirectMapping(string destMember, string destScript) 71 | { 72 | 73 | _noAssignmentMemberMappings[destMember] = destScript; 74 | 75 | } 76 | 77 | 78 | 79 | 80 | /// 81 | /// 获取脚本 82 | /// 83 | public static string Script 84 | { 85 | 86 | get 87 | { 88 | 89 | StringBuilder builder = new StringBuilder(_init); 90 | foreach (var item in _needAssignmentMemberMappings) 91 | { 92 | 93 | if (item.Value != default) 94 | { 95 | builder.AppendLine($"dest.{item.Key} = {item.Value};"); 96 | } 97 | 98 | } 99 | foreach (var item in _noAssignmentMemberMappings) 100 | { 101 | 102 | if (item.Value != default) 103 | { 104 | builder.Append(item.Value); 105 | if (!item.Value.EndsWith(";")) 106 | { 107 | builder.Append(';'); 108 | } 109 | } 110 | 111 | } 112 | builder.AppendLine("return dest;"); 113 | return builder.ToString(); 114 | 115 | } 116 | 117 | } 118 | 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /NMapperBechmark/NMapperBechmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NMapperBechmark/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NMapperBechmark 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | Console.WriteLine("Hello World!"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /NMapperUT/NMapperUT.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /NMapperUT/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace NMapperUT 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NMapper 2 | 3 | 实体类映射 4 | 5 | 6 | 7 | 8 | ## 需求讨论 9 | 10 | 欢迎大家积极提 [issue](https://github.com/night-moon-studio/NMapper/issues/new) 11 | 12 | ## 团队成员 13 | 14 | 15 | 16 | ## OnlineChart 17 | 18 | 加入我们,参与讨论,贡献代码,成为组内一员,另欢迎在线与我们讨论:[Gitter](https://gitter.im/NMS-NMapper/community) 19 | 20 | ## License 21 | NMapper is licensed under the [MIT license](LICENSE). 22 | 23 | 24 | -------------------------------------------------------------------------------- /SelfTest/Model/Single/SingleDestModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SelfTest.Model 4 | { 5 | public class SingleDestModel 6 | { 7 | public string Name; 8 | public string Title { get; set; } 9 | 10 | public int Age; 11 | public int Money { get; set; } 12 | 13 | public DateTime Time { get; set; } 14 | public decimal LongLongAgo; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SelfTest/Model/Single/SingleSrcModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SelfTest.Model 4 | { 5 | public class SingleSrcModel 6 | { 7 | public string Name; 8 | public string Title { get; set; } 9 | 10 | public int Age; 11 | public int Money { get; set; } 12 | 13 | public DateTime Time { get; set; } 14 | public decimal LongLongAgo; 15 | public int Sss; 16 | public string Aaa; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SelfTest/Model/SingleConvert/SingleConvertDestModel.cs: -------------------------------------------------------------------------------- 1 | namespace SelfTest.Model 2 | { 3 | public class SingleConvertDestModel 4 | { 5 | public string Name; 6 | public string Title { get; set; } 7 | 8 | public double Age; 9 | public float Money { get; set; } 10 | 11 | public string Time { get; set; } 12 | public string LongLongAgo; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SelfTest/Model/SingleConvert/SingleConvertSrcModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SelfTest.Model 4 | { 5 | public class SingleConvertSrcModel 6 | { 7 | public string Name; 8 | public string Title { get; set; } 9 | 10 | public int Age; 11 | public int Money { get; set; } 12 | 13 | public DateTime Time { get; set; } 14 | public decimal LongLongAgo; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SelfTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NMapper; 3 | using SelfTest.Model; 4 | 5 | namespace SelfTest 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | SingleSrcModel model = new SingleSrcModel(); 12 | model.Age = 100; 13 | model.LongLongAgo = 1111111111; 14 | model.Money = 1000; 15 | model.Name = "小明"; 16 | model.Time = DateTime.Now; 17 | model.Title = "gagagag"; 18 | var result = MapperOperator.MapFrom(model); 19 | var result1 = MapperOperator.MapFrom(result); 20 | var result2 = MapperOperator.MapFrom(result1); 21 | 22 | SingleConvertSrcModel a = new SingleConvertSrcModel(); 23 | a.Age = Convert.ToInt32(result1.Age); 24 | Console.ReadKey(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SelfTest/SelfTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | true 7 | 8 | 9 | 10 | TRACE;DEBUG 11 | 12 | 13 | 14 | TRACE 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------