├── .azure └── pipelines │ └── ci.yml ├── .editorconfig ├── .github └── dependabot.yml ├── .gitignore ├── LICENSE.md ├── Madd0.AzureStorageDriver.sln ├── Madd0.AzureStorageDriver ├── AzureDriver.cs ├── Compiler │ ├── CodeDomCompiler.cs │ ├── ICompiler.cs │ └── RoslynCompiler.cs ├── ConnectionDialog.xaml ├── ConnectionDialog.xaml.cs ├── DataContextTemplate.cs ├── DataContextTemplate.tt ├── Madd0.AzureStorageDriver.csproj ├── Model │ ├── AzureEnvironment.cs │ ├── CloudTable.cs │ ├── GenericEntity.cs │ ├── StorageAccountProperties.cs │ └── TableColumn.cs ├── Properties │ ├── Exceptions.Designer.cs │ ├── Exceptions.resx │ ├── Resources.Designer.cs │ └── Resources.resx ├── SchemaBuilder.cs ├── UserQuery │ └── ExtendedTableQuery.cs └── header.xml ├── README.md └── Settings.XamlStyler /.azure/pipelines/ci.yml: -------------------------------------------------------------------------------- 1 | # .NET Desktop 2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net 5 | 6 | # Configure which branches trigger builds 7 | trigger: 8 | batch: true 9 | branches: 10 | include: 11 | - master 12 | - develop 13 | paths: 14 | exclude: 15 | - .azure/* 16 | - LICENSE.md 17 | - README.md 18 | 19 | # Run PR validation on all branches 20 | pr: 21 | autoCancel: true 22 | branches: 23 | include: 24 | - '*' 25 | 26 | pool: 27 | vmImage: 'windows-latest' 28 | 29 | variables: 30 | solution: '**/*.sln' 31 | buildPlatform: 'Any CPU' 32 | buildConfiguration: 'Release' 33 | 34 | steps: 35 | - task: NuGetToolInstaller@1 36 | 37 | - task: NuGetCommand@2 38 | inputs: 39 | restoreSolution: '$(solution)' 40 | 41 | - task: SonarCloudPrepare@1 42 | inputs: 43 | SonarCloud: 'SonarCloud New' 44 | organization: 'madd0' 45 | scannerMode: 'MSBuild' 46 | projectKey: 'madd0_AzureStorageDriver' 47 | projectName: 'madd0.AzureStorageDriver' 48 | extraProperties: | 49 | # Additional properties that will be passed to the scanner, 50 | # Put one key=value per line, example: 51 | # sonar.exclusions=**/*.bin 52 | sonar.organization=madd0 53 | 54 | - task: VSBuild@1 55 | inputs: 56 | solution: '$(solution)' 57 | msbuildArgs: '/p:BaseOutputPath=$(Build.BinariesDirectory)\\' 58 | platform: '$(buildPlatform)' 59 | configuration: '$(buildConfiguration)' 60 | 61 | - task: SonarCloudAnalyze@1 62 | 63 | - task: SonarCloudPublish@1 64 | inputs: 65 | pollingTimeoutSec: '300' 66 | 67 | - task: ArchiveFiles@2 68 | displayName: Create .NET 4.6 lpx 69 | inputs: 70 | rootFolderOrFile: '$(Build.BinariesDirectory)/$(buildConfiguration)/net46' 71 | includeRootFolder: false 72 | archiveType: 'zip' 73 | archiveFile: '$(Build.ArtifactStagingDirectory)/Madd0.AzureStorageDriver.lpx' 74 | replaceExistingArchive: true 75 | 76 | - task: ArchiveFiles@2 77 | displayName: Create .NET Core 3.1 lpx6 78 | inputs: 79 | rootFolderOrFile: '$(Build.BinariesDirectory)/$(buildConfiguration)/netcoreapp3.1' 80 | includeRootFolder: false 81 | archiveType: 'zip' 82 | archiveFile: '$(Build.ArtifactStagingDirectory)/Madd0.AzureStorageDriver.netcore31.lpx6' 83 | replaceExistingArchive: true 84 | 85 | - task: CopyFiles@2 86 | inputs: 87 | SourceFolder: '$(Build.BinariesDirectory)/$(buildConfiguration)' 88 | Contents: | 89 | *.nupkg 90 | *.snupkg 91 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 92 | 93 | - task: PublishPipelineArtifact@1 94 | inputs: 95 | targetPath: '$(Build.ArtifactStagingDirectory)' 96 | publishLocation: 'pipeline' 97 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = space 12 | tab_width = 4 13 | 14 | # New line preferences 15 | end_of_line = crlf 16 | insert_final_newline = false 17 | 18 | #### .NET Coding Conventions #### 19 | 20 | # Organize usings 21 | dotnet_separate_import_directive_groups = false 22 | dotnet_sort_system_directives_first = false 23 | 24 | # this. and Me. preferences 25 | dotnet_style_qualification_for_event = true:silent 26 | dotnet_style_qualification_for_field = false:silent 27 | dotnet_style_qualification_for_method = true:silent 28 | dotnet_style_qualification_for_property = true:silent 29 | 30 | # Language keywords vs BCL types preferences 31 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 32 | dotnet_style_predefined_type_for_member_access = true:silent 33 | 34 | # Parentheses preferences 35 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 36 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 37 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 38 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 39 | 40 | # Modifier preferences 41 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 42 | 43 | # Expression-level preferences 44 | csharp_style_deconstructed_variable_declaration = true:suggestion 45 | csharp_style_inlined_variable_declaration = true:suggestion 46 | csharp_style_throw_expression = true:suggestion 47 | dotnet_style_coalesce_expression = true:suggestion 48 | dotnet_style_collection_initializer = true:suggestion 49 | dotnet_style_explicit_tuple_names = true:suggestion 50 | dotnet_style_null_propagation = true:suggestion 51 | dotnet_style_object_initializer = true:suggestion 52 | dotnet_style_prefer_auto_properties = true:suggestion 53 | dotnet_style_prefer_compound_assignment = true:suggestion 54 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 55 | dotnet_style_prefer_conditional_expression_over_return = true:silent 56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 57 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 59 | 60 | # Field preferences 61 | dotnet_style_readonly_field = true:suggestion 62 | 63 | # Parameter preferences 64 | dotnet_code_quality_unused_parameters = all:suggestion 65 | 66 | #### C# Coding Conventions #### 67 | 68 | # var preferences 69 | csharp_style_var_elsewhere = false:silent 70 | csharp_style_var_for_built_in_types = true:silent 71 | csharp_style_var_when_type_is_apparent = true:silent 72 | 73 | # Expression-bodied members 74 | csharp_style_expression_bodied_accessors = true:silent 75 | csharp_style_expression_bodied_constructors = false:silent 76 | csharp_style_expression_bodied_indexers = true:silent 77 | csharp_style_expression_bodied_lambdas = true:silent 78 | csharp_style_expression_bodied_local_functions = false:silent 79 | csharp_style_expression_bodied_methods = false:silent 80 | csharp_style_expression_bodied_operators = false:silent 81 | csharp_style_expression_bodied_properties = true:silent 82 | 83 | # Pattern matching preferences 84 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 85 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 86 | csharp_style_prefer_switch_expression = true:suggestion 87 | 88 | # Null-checking preferences 89 | csharp_style_conditional_delegate_call = true:suggestion 90 | 91 | # Modifier preferences 92 | csharp_prefer_static_local_function = true:suggestion 93 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 94 | 95 | # Code-block preferences 96 | csharp_prefer_braces = true:suggestion 97 | csharp_prefer_simple_using_statement = true:suggestion 98 | 99 | # Expression-level preferences 100 | csharp_prefer_simple_default_expression = true:suggestion 101 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 102 | csharp_style_prefer_index_operator = true:suggestion 103 | csharp_style_prefer_range_operator = true:suggestion 104 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 105 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 106 | 107 | # 'using' directive preferences 108 | csharp_using_directive_placement = outside_namespace:silent 109 | 110 | #### C# Formatting Rules #### 111 | 112 | # New line preferences 113 | csharp_new_line_before_catch = true 114 | csharp_new_line_before_else = true 115 | csharp_new_line_before_finally = true 116 | csharp_new_line_before_members_in_anonymous_types = true 117 | csharp_new_line_before_members_in_object_initializers = true 118 | csharp_new_line_before_open_brace = all 119 | csharp_new_line_between_query_expression_clauses = true 120 | 121 | # Indentation preferences 122 | csharp_indent_block_contents = true 123 | csharp_indent_braces = false 124 | csharp_indent_case_contents = true 125 | csharp_indent_case_contents_when_block = true 126 | csharp_indent_labels = one_less_than_current 127 | csharp_indent_switch_labels = true 128 | 129 | # Space preferences 130 | csharp_space_after_cast = false 131 | csharp_space_after_colon_in_inheritance_clause = true 132 | csharp_space_after_comma = true 133 | csharp_space_after_dot = false 134 | csharp_space_after_keywords_in_control_flow_statements = true 135 | csharp_space_after_semicolon_in_for_statement = true 136 | csharp_space_around_binary_operators = before_and_after 137 | csharp_space_around_declaration_statements = false 138 | csharp_space_before_colon_in_inheritance_clause = true 139 | csharp_space_before_comma = false 140 | csharp_space_before_dot = false 141 | csharp_space_before_open_square_brackets = false 142 | csharp_space_before_semicolon_in_for_statement = false 143 | csharp_space_between_empty_square_brackets = false 144 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 145 | csharp_space_between_method_call_name_and_opening_parenthesis = false 146 | csharp_space_between_method_call_parameter_list_parentheses = false 147 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 148 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 149 | csharp_space_between_method_declaration_parameter_list_parentheses = false 150 | csharp_space_between_parentheses = false 151 | csharp_space_between_square_brackets = false 152 | 153 | # Wrapping preferences 154 | csharp_preserve_single_line_blocks = true 155 | csharp_preserve_single_line_statements = true 156 | 157 | #### Naming styles #### 158 | 159 | # Naming rules 160 | 161 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 162 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 163 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 164 | 165 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 166 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 167 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 168 | 169 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 170 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 171 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 172 | 173 | dotnet_naming_rule.constant_field_should_be_pascal_case.severity = suggestion 174 | dotnet_naming_rule.constant_field_should_be_pascal_case.symbols = constant_field 175 | dotnet_naming_rule.constant_field_should_be_pascal_case.style = pascal_case 176 | 177 | dotnet_naming_rule.static_field_should_be_pascal_case.severity = suggestion 178 | dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field 179 | dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case 180 | 181 | dotnet_naming_rule.private_or_internal_field_should_be_underscore_prefix.severity = warning 182 | dotnet_naming_rule.private_or_internal_field_should_be_underscore_prefix.symbols = private_or_internal_field 183 | dotnet_naming_rule.private_or_internal_field_should_be_underscore_prefix.style = underscore_prefix 184 | 185 | dotnet_naming_rule.parameter_should_be_prefixed_parameter.severity = silent 186 | dotnet_naming_rule.parameter_should_be_prefixed_parameter.symbols = parameter 187 | dotnet_naming_rule.parameter_should_be_prefixed_parameter.style = prefixed_parameter 188 | 189 | # Symbol specifications 190 | 191 | dotnet_naming_symbols.interface.applicable_kinds = interface 192 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal 193 | dotnet_naming_symbols.interface.required_modifiers = 194 | 195 | dotnet_naming_symbols.static_field.applicable_kinds = field 196 | dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal 197 | dotnet_naming_symbols.static_field.required_modifiers = static 198 | 199 | dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field 200 | dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private 201 | dotnet_naming_symbols.private_or_internal_field.required_modifiers = 202 | 203 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 204 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal 205 | dotnet_naming_symbols.types.required_modifiers = 206 | 207 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 208 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal 209 | dotnet_naming_symbols.non_field_members.required_modifiers = 210 | 211 | dotnet_naming_symbols.constant_field.applicable_kinds = field 212 | dotnet_naming_symbols.constant_field.applicable_accessibilities = public, internal, private, protected, protected_internal 213 | dotnet_naming_symbols.constant_field.required_modifiers = const 214 | 215 | dotnet_naming_symbols.parameter.applicable_kinds = parameter 216 | dotnet_naming_symbols.parameter.applicable_accessibilities = 217 | dotnet_naming_symbols.parameter.required_modifiers = 218 | 219 | # Naming styles 220 | 221 | dotnet_naming_style.pascal_case.required_prefix = 222 | dotnet_naming_style.pascal_case.required_suffix = 223 | dotnet_naming_style.pascal_case.word_separator = 224 | dotnet_naming_style.pascal_case.capitalization = pascal_case 225 | 226 | dotnet_naming_style.begins_with_i.required_prefix = I 227 | dotnet_naming_style.begins_with_i.required_suffix = 228 | dotnet_naming_style.begins_with_i.word_separator = 229 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 230 | 231 | dotnet_naming_style.underscore_prefix.required_prefix = _ 232 | dotnet_naming_style.underscore_prefix.required_suffix = 233 | dotnet_naming_style.underscore_prefix.word_separator = 234 | dotnet_naming_style.underscore_prefix.capitalization = camel_case 235 | 236 | dotnet_naming_style.prefixed_parameter.required_prefix = p 237 | dotnet_naming_style.prefixed_parameter.required_suffix = 238 | dotnet_naming_style.prefixed_parameter.word_separator = 239 | dotnet_naming_style.prefixed_parameter.capitalization = pascal_case 240 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.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 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Mauricio DIAZ ORLICH 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. -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29509.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Madd0.AzureStorageDriver", "Madd0.AzureStorageDriver\Madd0.AzureStorageDriver.csproj", "{33CB981E-3A8D-4949-9B34-F7F0B6B5891F}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{60F155B6-550D-41F3-A8DB-67ECA237869C}" 9 | ProjectSection(SolutionItems) = preProject 10 | LICENSE.md = LICENSE.md 11 | README.md = README.md 12 | EndProjectSection 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 | {33CB981E-3A8D-4949-9B34-F7F0B6B5891F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {33CB981E-3A8D-4949-9B34-F7F0B6B5891F}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {33CB981E-3A8D-4949-9B34-F7F0B6B5891F}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {33CB981E-3A8D-4949-9B34-F7F0B6B5891F}.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 = {887BB131-BC0A-45C2-A971-797FC9825D8C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/AzureDriver.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Reflection; 14 | using LINQPad.Extensibility.DataContext; 15 | using Madd0.AzureStorageDriver.Properties; 16 | #if NETCORE 17 | using Microsoft.Azure.Cosmos.Table; 18 | #else 19 | using Microsoft.Azure.CosmosDB.Table; 20 | #endif 21 | 22 | /// 23 | /// LINQPad dynamic driver that lets users connect to an Azure Table Storage account. 24 | /// 25 | public class AzureDriver : DynamicDataContextDriver 26 | { 27 | #if DEBUG 28 | static AzureDriver() 29 | { 30 | AppDomain.CurrentDomain.FirstChanceException += (sender, args) => 31 | { 32 | if (args.Exception.StackTrace.Contains(typeof(AzureDriver).Namespace)) 33 | { 34 | System.Diagnostics.Debugger.Launch(); 35 | } 36 | }; 37 | 38 | } 39 | #endif 40 | /// 41 | /// Gets the name of the driver author. 42 | /// 43 | public override string Author 44 | { 45 | get { return Resources.AuthorName; } 46 | } 47 | 48 | /// 49 | /// Gets the name of the driver. 50 | /// 51 | public override string Name 52 | { 53 | get { return Resources.DriverName; } 54 | } 55 | 56 | /// Gets the text to display in the root Schema Explorer node for a given connection info. 57 | /// The connection information. 58 | /// The text to display in the root Schema Explorer node for a given connection info 59 | public override string GetConnectionDescription(IConnectionInfo cxInfo) 60 | { 61 | var accountProperties = new StorageAccountProperties(cxInfo); 62 | 63 | var description = accountProperties.DisplayName; 64 | 65 | if (accountProperties.AzureEnvironment != AzureEnvironment.AzureGlobalCloud) 66 | { 67 | description += $" ({accountProperties.AzureEnvironment.Name})"; 68 | } 69 | 70 | return description; 71 | } 72 | 73 | /// 74 | /// Shows the connection dialog. 75 | /// 76 | /// The connection info. 77 | /// if set to true [is new connection]. 78 | /// 79 | public override bool ShowConnectionDialog(IConnectionInfo cxInfo, ConnectionDialogOptions dialogOptions) 80 | { 81 | if (dialogOptions.IsNewConnection) 82 | { 83 | _ = new StorageAccountProperties(cxInfo) 84 | { 85 | UseHttps = true, 86 | NumberOfRows = 100 87 | }; 88 | } 89 | 90 | bool? result = new ConnectionDialog(cxInfo).ShowDialog(); 91 | return result == true; 92 | } 93 | 94 | /// 95 | /// Determines whether two repositories are equivalent. 96 | /// 97 | /// The connection information of the first repository. 98 | /// The connection information of the second repository. 99 | /// true if both repositories use the same account name; false otherwise. 100 | public override bool AreRepositoriesEquivalent(IConnectionInfo c1, IConnectionInfo c2) 101 | { 102 | var account1 = (string)c1.DriverData.Element("AccountName") ?? string.Empty; 103 | var account2 = (string)c2.DriverData.Element("AccountName") ?? string.Empty; 104 | 105 | return account1.Equals(account2); 106 | } 107 | 108 | #if NETCORE 109 | public override IEnumerable GetAssembliesToAdd(IConnectionInfo cxInfo) 110 | { 111 | return new string[] 112 | { 113 | "Microsoft.Azure.Cosmos.Table.dll" 114 | }; 115 | } 116 | #else 117 | public override IEnumerable GetAssembliesToAdd(IConnectionInfo cxInfo) 118 | { 119 | return new string[] 120 | { 121 | "Microsoft.Azure.CosmosDB.Table.dll" 122 | }; 123 | } 124 | #endif 125 | /// 126 | /// Gets the schema and builds the assembly that contains the typed data context. 127 | /// 128 | /// The connection information. 129 | /// The assembly to build. 130 | /// The namespace to be used in the generated code. 131 | /// Name of the type of the typed data context. 132 | /// A list of instaces that describes the current schema. 133 | public override List GetSchemaAndBuildAssembly(IConnectionInfo cxInfo, AssemblyName assemblyToBuild, ref string nameSpace, ref string typeName) 134 | { 135 | // The helper class SchemaBuilder will do the heavy lifting 136 | return SchemaBuilder.GetSchemaAndBuildAssembly( 137 | new StorageAccountProperties(cxInfo), 138 | assemblyToBuild, 139 | nameSpace, 140 | typeName); 141 | } 142 | 143 | /// 144 | /// Gets the context constructor arguments. 145 | /// 146 | /// The connection info. 147 | /// An ordered collection of objects to pass to the data context as arguments. 148 | public override object[] GetContextConstructorArguments(IConnectionInfo cxInfo) 149 | { 150 | var properties = new StorageAccountProperties(cxInfo); 151 | 152 | var storageAccount = properties.GetStorageAccount(); 153 | 154 | return new object[] 155 | { 156 | storageAccount.CreateCloudTableClient() 157 | }; 158 | } 159 | 160 | /// 161 | /// Gets the context constructor parameters. 162 | /// 163 | /// The connection info. 164 | /// A list of objects that describe the parameters 165 | /// of the typed data context's constructor. 166 | public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo cxInfo) 167 | { 168 | return new[] 169 | { 170 | new ParameterDescriptor("client", typeof(CloudTableClient).FullName) 171 | }; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Compiler/CodeDomCompiler.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | #if !NETCORE 10 | namespace Madd0.AzureStorageDriver 11 | { 12 | using Madd0.AzureStorageDriver.Properties; 13 | using Microsoft.CSharp; 14 | using System; 15 | using System.CodeDom.Compiler; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Reflection; 19 | 20 | internal class CodeDomCompiler : ICompiler 21 | { 22 | public void Compile(string code, AssemblyName name, IEnumerable assemblyLocations) 23 | { 24 | CompilerResults results; 25 | 26 | var path = name.CodeBase; 27 | 28 | var dependencies = assemblyLocations.ToArray(); 29 | 30 | using (var codeProvider = new CSharpCodeProvider()) 31 | { 32 | #if DEBUG 33 | var options = new CompilerParameters(dependencies, path, true); 34 | #else 35 | var options = new CompilerParameters(dependencies, path, false); 36 | #endif 37 | results = codeProvider.CompileAssemblyFromSource(options, code); 38 | } 39 | 40 | if (results.Errors.Count > 0) 41 | { 42 | throw new ArgumentException(string.Format(Exceptions.CannotCompileCode, results.Errors[0].ErrorText, results.Errors[0].Line)); 43 | } 44 | } 45 | } 46 | } 47 | #endif -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Compiler/ICompiler.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | internal interface ICompiler 15 | { 16 | void Compile(string code, AssemblyName name, IEnumerable assemblyLocations); 17 | } 18 | } -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Compiler/RoslynCompiler.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | #if NETCORE 10 | 11 | namespace Madd0.AzureStorageDriver 12 | { 13 | using LINQPad.Extensibility.DataContext; 14 | using Madd0.AzureStorageDriver.Properties; 15 | using Microsoft.CodeAnalysis; 16 | using Microsoft.CodeAnalysis.CSharp; 17 | using System; 18 | using System.Collections.Generic; 19 | using System.IO; 20 | using System.Linq; 21 | using System.Reflection; 22 | 23 | internal class RoslynCompiler : ICompiler 24 | { 25 | public CSharpCompilationOptions Options { get; } = new CSharpCompilationOptions( 26 | OutputKind.DynamicallyLinkedLibrary, 27 | reportSuppressedDiagnostics: true, 28 | #if DEBUG 29 | optimizationLevel: OptimizationLevel.Debug, 30 | #else 31 | optimizationLevel: OptimizationLevel.Release, 32 | #endif 33 | generalDiagnosticOption: ReportDiagnostic.Error 34 | ); 35 | 36 | public void Compile(string code, AssemblyName name, IEnumerable assemblyLocations) 37 | { 38 | var platformAssemblies = DataContextDriver.GetCoreFxReferenceAssemblies(); 39 | 40 | var references = platformAssemblies.Concat(assemblyLocations).Select(l => MetadataReference.CreateFromFile(l)); 41 | 42 | var parseOptions = new CSharpParseOptions().WithPreprocessorSymbols("NETCORE"); 43 | 44 | var compilation = CSharpCompilation.Create( 45 | name.Name, 46 | references: references, 47 | syntaxTrees: new SyntaxTree[] { CSharpSyntaxTree.ParseText(code, parseOptions) }, 48 | options: this.Options 49 | ); 50 | 51 | using var fileStream = File.OpenWrite(name.CodeBase); 52 | 53 | var results = compilation.Emit(fileStream); 54 | 55 | if (!results.Success) 56 | { 57 | throw new ArgumentException(string.Format(Exceptions.CannotCompileCode, results.Diagnostics[0].GetMessage(), results.Diagnostics[0].Location.GetLineSpan())); 58 | } 59 | } 60 | } 61 | } 62 | 63 | #endif -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/ConnectionDialog.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | 39 | 40 | 41 | 42 | 45 | 46 | 50 | 51 | 52 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | 84 | 89 | 90 | 95 | 103 | 107 | 108 | 109 | The number of lines that will be read in order to determine the schema of an Azure Table. 110 | 111 | 112 | 115 | 120 | 121 | 122 | 127 | 135 | 139 | 140 | 141 | The number of concurrent calls that will be made to Azure Table Storage to determine the columns available for each table. 142 | 143 | 144 | 147 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 164 | 165 | 166 | 172 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/ConnectionDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | using System; 12 | using System.Runtime.InteropServices; 13 | using System.Windows; 14 | using System.Windows.Interop; 15 | using LINQPad.Extensibility.DataContext; 16 | 17 | /// 18 | /// Interaction logic for ConnectionDialog.xaml 19 | /// 20 | public partial class ConnectionDialog : Window 21 | { 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The connection info. 26 | public ConnectionDialog(IConnectionInfo connectionInfo) 27 | { 28 | InitializeComponent(); 29 | 30 | DataContext = new StorageAccountProperties(connectionInfo); 31 | } 32 | 33 | /// 34 | /// Raises the event. 35 | /// 36 | /// An that contains the event data. 37 | protected override void OnSourceInitialized(EventArgs e) 38 | { 39 | IntPtr hwnd = new WindowInteropHelper(this).Handle; 40 | 41 | // Change the window style to remove icon and buttons 42 | SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & (0xFFFFFFFF ^ WS_SYSMENU)); 43 | 44 | base.OnSourceInitialized(e); 45 | } 46 | 47 | /// 48 | /// Called when the OK button is clicked. 49 | /// 50 | /// The sender. 51 | /// The instance containing the event data. 52 | private void OnOkClick(object sender, RoutedEventArgs e) 53 | { 54 | this.DialogResult = true; 55 | } 56 | 57 | #region p/invoke 58 | 59 | [DllImport("user32.dll")] 60 | private static extern uint GetWindowLong(IntPtr hWnd, int nIndex); 61 | 62 | [DllImport("user32.dll")] 63 | private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong); 64 | 65 | private const int GWL_STYLE = -16; 66 | 67 | private const uint WS_SYSMENU = 0x80000; 68 | 69 | #endregion 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/DataContextTemplate.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version: 16.0.0.0 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | // ------------------------------------------------------------------------------ 10 | namespace Madd0.AzureStorageDriver 11 | { 12 | using System; 13 | using System.Collections; 14 | using System.Collections.Generic; 15 | 16 | /// 17 | /// Class to produce the template output 18 | /// 19 | 20 | #line 1 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] 22 | public partial class DataContextTemplate : DataContextTemplateBase 23 | { 24 | #line hidden 25 | /// 26 | /// Create the template output 27 | /// 28 | public virtual string TransformText() 29 | { 30 | this.Write("\r\nnamespace "); 31 | 32 | #line 6 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 33 | this.Write(this.ToStringHelper.ToStringWithCulture(this.Namespace)); 34 | 35 | #line default 36 | #line hidden 37 | this.Write(@" 38 | { 39 | using System; 40 | using System.Collections; 41 | using System.Collections.Generic; 42 | using System.Linq; 43 | using Madd0.UserQuery; 44 | #if NETCORE 45 | using Microsoft.Azure.Cosmos.Table; 46 | #else 47 | using Microsoft.Azure.CosmosDB.Table; 48 | #endif 49 | 50 | public class "); 51 | 52 | #line 19 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 53 | this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName)); 54 | 55 | #line default 56 | #line hidden 57 | this.Write("\r\n {\r\n public "); 58 | 59 | #line 21 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 60 | this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName)); 61 | 62 | #line default 63 | #line hidden 64 | this.Write("(CloudTableClient client)\r\n {\r\n this.TableClient = client;\r\n " + 65 | " }\r\n\r\n public CloudTableClient TableClient\r\n {\r\n g" + 66 | "et;\r\n private set;\r\n }\r\n\r\n"); 67 | 68 | #line 32 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 69 | 70 | foreach (var table in this.Tables) 71 | { 72 | 73 | 74 | #line default 75 | #line hidden 76 | this.Write(" public ExtendedTableQuery<"); 77 | 78 | #line 36 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 79 | this.Write(this.ToStringHelper.ToStringWithCulture(table.Name)); 80 | 81 | #line default 82 | #line hidden 83 | this.Write("Entity> "); 84 | 85 | #line 36 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 86 | this.Write(this.ToStringHelper.ToStringWithCulture(table.Name)); 87 | 88 | #line default 89 | #line hidden 90 | this.Write("\r\n {\r\n get\r\n {\r\n return new ExtendedT" + 91 | "ableQuery<"); 92 | 93 | #line 40 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 94 | this.Write(this.ToStringHelper.ToStringWithCulture(table.Name)); 95 | 96 | #line default 97 | #line hidden 98 | this.Write("Entity>(this.TableClient.GetTableReference(\""); 99 | 100 | #line 40 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 101 | this.Write(this.ToStringHelper.ToStringWithCulture(table.Name)); 102 | 103 | #line default 104 | #line hidden 105 | this.Write("\"));\r\n }\r\n }\r\n"); 106 | 107 | #line 43 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 108 | 109 | } 110 | 111 | 112 | #line default 113 | #line hidden 114 | this.Write("\r\n public TableQuery MakeTableQuery()\r\n whe" + 115 | "re TElement : ITableEntity\r\n {\r\n return new TableQuery();\r\n }\r\n }\r\n\r\n"); 117 | 118 | #line 54 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 119 | 120 | foreach (var table in this.Tables) 121 | { 122 | 123 | 124 | #line default 125 | #line hidden 126 | this.Write(" public class "); 127 | 128 | #line 58 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 129 | this.Write(this.ToStringHelper.ToStringWithCulture(table.Name)); 130 | 131 | #line default 132 | #line hidden 133 | this.Write("Entity : TableEntity\r\n {\r\n "); 134 | 135 | #line 60 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 136 | 137 | foreach (var column in table.Columns) 138 | { 139 | if (!this.DefaultProperties.Contains(column.Name)) 140 | { 141 | 142 | 143 | #line default 144 | #line hidden 145 | this.Write(" public "); 146 | 147 | #line 66 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 148 | this.Write(this.ToStringHelper.ToStringWithCulture(column.TypeName)); 149 | 150 | #line default 151 | #line hidden 152 | this.Write(" "); 153 | 154 | #line 66 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 155 | this.Write(this.ToStringHelper.ToStringWithCulture(column.Name)); 156 | 157 | #line default 158 | #line hidden 159 | this.Write("\r\n {\r\n get;\r\n set;\r\n }\r\n "); 160 | 161 | #line 71 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 162 | 163 | } 164 | } 165 | 166 | 167 | #line default 168 | #line hidden 169 | this.Write(" }\r\n"); 170 | 171 | #line 76 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 172 | 173 | } 174 | 175 | 176 | #line default 177 | #line hidden 178 | this.Write("}\r\n\r\n"); 179 | return this.GenerationEnvironment.ToString(); 180 | } 181 | 182 | #line 81 "C:\Projects\AzureStorageDriver\Madd0.AzureStorageDriver\DataContextTemplate.tt" 183 | 184 | private readonly List DefaultProperties = new List { "PartitionKey", "RowKey", "Timestamp", "ETag" }; 185 | 186 | public string Namespace { get; set; } 187 | 188 | public string TypeName { get; set; } 189 | 190 | public IEnumerable Tables { get; set; } 191 | 192 | 193 | #line default 194 | #line hidden 195 | } 196 | 197 | #line default 198 | #line hidden 199 | #region Base class 200 | /// 201 | /// Base class for this transformation 202 | /// 203 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] 204 | public class DataContextTemplateBase 205 | { 206 | #region Fields 207 | private global::System.Text.StringBuilder generationEnvironmentField; 208 | private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; 209 | private global::System.Collections.Generic.List indentLengthsField; 210 | private string currentIndentField = ""; 211 | private bool endsWithNewline; 212 | private global::System.Collections.Generic.IDictionary sessionField; 213 | #endregion 214 | #region Properties 215 | /// 216 | /// The string builder that generation-time code is using to assemble generated output 217 | /// 218 | protected System.Text.StringBuilder GenerationEnvironment 219 | { 220 | get 221 | { 222 | if ((this.generationEnvironmentField == null)) 223 | { 224 | this.generationEnvironmentField = new global::System.Text.StringBuilder(); 225 | } 226 | return this.generationEnvironmentField; 227 | } 228 | set 229 | { 230 | this.generationEnvironmentField = value; 231 | } 232 | } 233 | /// 234 | /// The error collection for the generation process 235 | /// 236 | public System.CodeDom.Compiler.CompilerErrorCollection Errors 237 | { 238 | get 239 | { 240 | if ((this.errorsField == null)) 241 | { 242 | this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); 243 | } 244 | return this.errorsField; 245 | } 246 | } 247 | /// 248 | /// A list of the lengths of each indent that was added with PushIndent 249 | /// 250 | private System.Collections.Generic.List indentLengths 251 | { 252 | get 253 | { 254 | if ((this.indentLengthsField == null)) 255 | { 256 | this.indentLengthsField = new global::System.Collections.Generic.List(); 257 | } 258 | return this.indentLengthsField; 259 | } 260 | } 261 | /// 262 | /// Gets the current indent we use when adding lines to the output 263 | /// 264 | public string CurrentIndent 265 | { 266 | get 267 | { 268 | return this.currentIndentField; 269 | } 270 | } 271 | /// 272 | /// Current transformation session 273 | /// 274 | public virtual global::System.Collections.Generic.IDictionary Session 275 | { 276 | get 277 | { 278 | return this.sessionField; 279 | } 280 | set 281 | { 282 | this.sessionField = value; 283 | } 284 | } 285 | #endregion 286 | #region Transform-time helpers 287 | /// 288 | /// Write text directly into the generated output 289 | /// 290 | public void Write(string textToAppend) 291 | { 292 | if (string.IsNullOrEmpty(textToAppend)) 293 | { 294 | return; 295 | } 296 | // If we're starting off, or if the previous text ended with a newline, 297 | // we have to append the current indent first. 298 | if (((this.GenerationEnvironment.Length == 0) 299 | || this.endsWithNewline)) 300 | { 301 | this.GenerationEnvironment.Append(this.currentIndentField); 302 | this.endsWithNewline = false; 303 | } 304 | // Check if the current text ends with a newline 305 | if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) 306 | { 307 | this.endsWithNewline = true; 308 | } 309 | // This is an optimization. If the current indent is "", then we don't have to do any 310 | // of the more complex stuff further down. 311 | if ((this.currentIndentField.Length == 0)) 312 | { 313 | this.GenerationEnvironment.Append(textToAppend); 314 | return; 315 | } 316 | // Everywhere there is a newline in the text, add an indent after it 317 | textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); 318 | // If the text ends with a newline, then we should strip off the indent added at the very end 319 | // because the appropriate indent will be added when the next time Write() is called 320 | if (this.endsWithNewline) 321 | { 322 | this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); 323 | } 324 | else 325 | { 326 | this.GenerationEnvironment.Append(textToAppend); 327 | } 328 | } 329 | /// 330 | /// Write text directly into the generated output 331 | /// 332 | public void WriteLine(string textToAppend) 333 | { 334 | this.Write(textToAppend); 335 | this.GenerationEnvironment.AppendLine(); 336 | this.endsWithNewline = true; 337 | } 338 | /// 339 | /// Write formatted text directly into the generated output 340 | /// 341 | public void Write(string format, params object[] args) 342 | { 343 | this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); 344 | } 345 | /// 346 | /// Write formatted text directly into the generated output 347 | /// 348 | public void WriteLine(string format, params object[] args) 349 | { 350 | this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); 351 | } 352 | /// 353 | /// Raise an error 354 | /// 355 | public void Error(string message) 356 | { 357 | System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); 358 | error.ErrorText = message; 359 | this.Errors.Add(error); 360 | } 361 | /// 362 | /// Raise a warning 363 | /// 364 | public void Warning(string message) 365 | { 366 | System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); 367 | error.ErrorText = message; 368 | error.IsWarning = true; 369 | this.Errors.Add(error); 370 | } 371 | /// 372 | /// Increase the indent 373 | /// 374 | public void PushIndent(string indent) 375 | { 376 | if ((indent == null)) 377 | { 378 | throw new global::System.ArgumentNullException("indent"); 379 | } 380 | this.currentIndentField = (this.currentIndentField + indent); 381 | this.indentLengths.Add(indent.Length); 382 | } 383 | /// 384 | /// Remove the last indent that was added with PushIndent 385 | /// 386 | public string PopIndent() 387 | { 388 | string returnValue = ""; 389 | if ((this.indentLengths.Count > 0)) 390 | { 391 | int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; 392 | this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); 393 | if ((indentLength > 0)) 394 | { 395 | returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); 396 | this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); 397 | } 398 | } 399 | return returnValue; 400 | } 401 | /// 402 | /// Remove any indentation 403 | /// 404 | public void ClearIndent() 405 | { 406 | this.indentLengths.Clear(); 407 | this.currentIndentField = ""; 408 | } 409 | #endregion 410 | #region ToString Helpers 411 | /// 412 | /// Utility class to produce culture-oriented representation of an object as a string. 413 | /// 414 | public class ToStringInstanceHelper 415 | { 416 | private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; 417 | /// 418 | /// Gets or sets format provider to be used by ToStringWithCulture method. 419 | /// 420 | public System.IFormatProvider FormatProvider 421 | { 422 | get 423 | { 424 | return this.formatProviderField ; 425 | } 426 | set 427 | { 428 | if ((value != null)) 429 | { 430 | this.formatProviderField = value; 431 | } 432 | } 433 | } 434 | /// 435 | /// This is called from the compile/run appdomain to convert objects within an expression block to a string 436 | /// 437 | public string ToStringWithCulture(object objectToConvert) 438 | { 439 | if ((objectToConvert == null)) 440 | { 441 | throw new global::System.ArgumentNullException("objectToConvert"); 442 | } 443 | System.Type t = objectToConvert.GetType(); 444 | System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { 445 | typeof(System.IFormatProvider)}); 446 | if ((method == null)) 447 | { 448 | return objectToConvert.ToString(); 449 | } 450 | else 451 | { 452 | return ((string)(method.Invoke(objectToConvert, new object[] { 453 | this.formatProviderField }))); 454 | } 455 | } 456 | } 457 | private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); 458 | /// 459 | /// Helper to produce culture-oriented representation of an object as a string 460 | /// 461 | public ToStringInstanceHelper ToStringHelper 462 | { 463 | get 464 | { 465 | return this.toStringHelperField; 466 | } 467 | } 468 | #endregion 469 | } 470 | #endregion 471 | } 472 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/DataContextTemplate.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ import namespace="System" #> 3 | <#@ import namespace="System.Collections" #> 4 | <#@ import namespace="System.Collections.Generic" #> 5 | 6 | namespace <#= this.Namespace #> 7 | { 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using Madd0.UserQuery; 13 | #if NETCORE 14 | using Microsoft.Azure.Cosmos.Table; 15 | #else 16 | using Microsoft.Azure.CosmosDB.Table; 17 | #endif 18 | 19 | public class <#= this.TypeName #> 20 | { 21 | public <#= this.TypeName #>(CloudTableClient client) 22 | { 23 | this.TableClient = client; 24 | } 25 | 26 | public CloudTableClient TableClient 27 | { 28 | get; 29 | private set; 30 | } 31 | 32 | <# 33 | foreach (var table in this.Tables) 34 | { 35 | #> 36 | public ExtendedTableQuery<<#= table.Name #>Entity> <#= table.Name #> 37 | { 38 | get 39 | { 40 | return new ExtendedTableQuery<<#= table.Name #>Entity>(this.TableClient.GetTableReference("<#= table.Name #>")); 41 | } 42 | } 43 | <# 44 | } 45 | #> 46 | 47 | public TableQuery MakeTableQuery() 48 | where TElement : ITableEntity 49 | { 50 | return new TableQuery(); 51 | } 52 | } 53 | 54 | <# 55 | foreach (var table in this.Tables) 56 | { 57 | #> 58 | public class <#= table.Name #>Entity : TableEntity 59 | { 60 | <# 61 | foreach (var column in table.Columns) 62 | { 63 | if (!this.DefaultProperties.Contains(column.Name)) 64 | { 65 | #> 66 | public <#= column.TypeName #> <#= column.Name #> 67 | { 68 | get; 69 | set; 70 | } 71 | <# 72 | } 73 | } 74 | #> 75 | } 76 | <# 77 | } 78 | #> 79 | } 80 | 81 | <#+ 82 | private readonly List DefaultProperties = new List { "PartitionKey", "RowKey", "Timestamp", "ETag" }; 83 | 84 | public string Namespace { get; set; } 85 | 86 | public string TypeName { get; set; } 87 | 88 | public IEnumerable Tables { get; set; } 89 | #> -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Madd0.AzureStorageDriver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1;net46 4 | latest 5 | true 6 | Madd0.AzureStorageDriver 7 | true 8 | linqpaddriver 9 | 10 | 11 | 12 | AzureStorageDriver 13 | A LINQPad driver to perform queries against Azure Table Storage. 14 | madd0 (http://www.madd0.com) 15 | Azure Table Storage Driver 16 | Azure Table Storage Driver 17 | Copyright © 2012 Mauricio DIAZ ORLICH 18 | 2.1.2 19 | 2.1.2.0 20 | 2.1.2.0 21 | LICENSE.md 22 | https://github.com/madd0/AzureStorageDriver 23 | https://github.com/madd0/AzureStorageDriver.git 24 | git 25 | Mauricio Díaz Orlich 26 | True 27 | snupkg 28 | 29 | 30 | 31 | NETCORE 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 4.0 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 1.0.8 52 | 53 | 54 | 55 | 56 | 57 | 58 | 2.1.2 59 | 60 | 61 | PreserveNewest 62 | 63 | 64 | 65 | 66 | 67 | TextTemplatingFilePreprocessor 68 | DataContextTemplate.cs 69 | 70 | 71 | True 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | True 84 | True 85 | DataContextTemplate.tt 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Model/AzureEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Madd0.AzureStorageDriver 6 | { 7 | // From: https://github.com/Azure/azure-libraries-for-net/blob/master/src/ResourceManagement/ResourceManager/AzureEnvironment.cs 8 | public class AzureEnvironment 9 | { 10 | static AzureEnvironment() 11 | { 12 | AzureGlobalCloud = new AzureEnvironment() 13 | { 14 | Name = "Global Cloud", 15 | AuthenticationEndpoint = "https://login.microsoftonline.com/", 16 | ResourceManagerEndpoint = "https://management.azure.com/", 17 | ManagementEndpoint = "https://management.core.windows.net/", 18 | GraphEndpoint = "https://graph.windows.net/", 19 | StorageEndpointSuffix = "core.windows.net", 20 | KeyVaultSuffix = "vault.azure.net" 21 | }; 22 | AzureChinaCloud = new AzureEnvironment() 23 | { 24 | Name = "China Cloud", 25 | AuthenticationEndpoint = "https://login.chinacloudapi.cn/", 26 | ResourceManagerEndpoint = "https://management.chinacloudapi.cn/", 27 | ManagementEndpoint = "https://management.core.chinacloudapi.cn/", 28 | GraphEndpoint = "https://graph.chinacloudapi.cn/", 29 | StorageEndpointSuffix = "core.chinacloudapi.cn", 30 | KeyVaultSuffix = "vault.azure.cn" 31 | }; 32 | AzureUSGovernment = new AzureEnvironment() 33 | { 34 | Name = "US Government", 35 | AuthenticationEndpoint = "https://login.microsoftonline.us/", 36 | ResourceManagerEndpoint = "https://management.usgovcloudapi.net/", 37 | ManagementEndpoint = "https://management.core.usgovcloudapi.net", 38 | GraphEndpoint = "https://graph.windows.net/", 39 | StorageEndpointSuffix = "core.usgovcloudapi.net", 40 | KeyVaultSuffix = "vault.usgovcloudapi.net" 41 | }; 42 | AzureGermanCloud = new AzureEnvironment() 43 | { 44 | Name = "German Cloud", 45 | AuthenticationEndpoint = "https://login.microsoftonline.de/", 46 | ResourceManagerEndpoint = "https://management.microsoftazure.de/", 47 | ManagementEndpoint = "https://management.core.cloudapi.de", 48 | GraphEndpoint = "https://graph.cloudapi.de/", 49 | StorageEndpointSuffix = "core.cloudapi.de", 50 | KeyVaultSuffix = "vault.microsoftazure.de" 51 | }; 52 | 53 | Environments = new Dictionary 54 | { 55 | [AzureGlobalCloud.Name] = AzureGlobalCloud, 56 | [AzureChinaCloud.Name] = AzureChinaCloud, 57 | [AzureUSGovernment.Name] = AzureUSGovernment, 58 | [AzureGermanCloud.Name] = AzureGermanCloud, 59 | }; 60 | } 61 | 62 | public static AzureEnvironment AzureGlobalCloud 63 | { 64 | get; private set; 65 | } 66 | 67 | public static AzureEnvironment AzureChinaCloud 68 | { 69 | get; private set; 70 | } 71 | 72 | public static AzureEnvironment AzureUSGovernment 73 | { 74 | get; private set; 75 | } 76 | 77 | public static AzureEnvironment AzureGermanCloud 78 | { 79 | get; private set; 80 | } 81 | 82 | public static IEnumerable KnownEnvironments 83 | { 84 | get 85 | { 86 | yield return AzureGlobalCloud; 87 | yield return AzureChinaCloud; 88 | yield return AzureUSGovernment; 89 | yield return AzureGermanCloud; 90 | } 91 | } 92 | 93 | public static Dictionary Environments 94 | { 95 | get; private set; 96 | } 97 | 98 | /// 99 | /// Name for the cloud environment 100 | /// 101 | public string Name { get; set; } 102 | 103 | /// 104 | /// Azure active directory service endpoint to get OAuth token to access ARM resource 105 | /// management endpoint . 106 | /// 107 | public string AuthenticationEndpoint { get; set; } 108 | 109 | /// 110 | /// Azure ARM resource management endpoint. 111 | /// 112 | public string ResourceManagerEndpoint { get; set; } 113 | 114 | /// 115 | /// Active Directory graph endpoint. 116 | /// 117 | public string GraphEndpoint { get; set; } 118 | 119 | /// 120 | /// Base URL for calls to service management (aka RDFE) and authentications to Active Directory. 121 | /// 122 | public string ManagementEndpoint { get; set; } 123 | 124 | /// 125 | /// The storage service url suffix. 126 | /// 127 | public string StorageEndpointSuffix { get; set; } 128 | 129 | /// 130 | /// The Key Vault service url suffix. 131 | /// 132 | public string KeyVaultSuffix { get; set; } 133 | 134 | /// 135 | /// Parse cloud environment name based on 136 | /// 137 | /// Cloud environment name 138 | /// An AzureEnvironment, or null if no matching environment found 139 | public static AzureEnvironment FromName(string name) 140 | => KnownEnvironments.FirstOrDefault(env => string.Equals(env.Name, name, StringComparison.OrdinalIgnoreCase)); 141 | } 142 | } -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Model/CloudTable.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | using System.Collections.Generic; 12 | 13 | /// 14 | /// Holds information about a table from Azure storage. 15 | /// 16 | public class CloudTable 17 | { 18 | /// 19 | /// Gets or sets the name of the table. 20 | /// 21 | public string Name 22 | { 23 | get; 24 | set; 25 | } 26 | 27 | /// 28 | /// Gets or sets the table's properties. 29 | /// 30 | public IEnumerable Columns 31 | { 32 | get; 33 | set; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Model/GenericEntity.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | #if NETCORE 12 | using Microsoft.Azure.Cosmos.Table; 13 | #else 14 | using Microsoft.Azure.CosmosDB.Table; 15 | #endif 16 | using System.Collections.Generic; 17 | 18 | /// 19 | /// Represents a generic entity from table storage. 20 | /// 21 | /// 22 | /// This class extends , which gives it the 23 | /// , and properties, 24 | /// but all other properties that are obtained from table storage are simply stored in its 25 | /// dictionary as string values, where the property name is the property in 26 | /// the dictionary. 27 | /// 28 | public class GenericEntity : TableEntity 29 | { 30 | 31 | /// 32 | /// Gets or sets the table name. 33 | /// 34 | /// The table name. 35 | public string TableName 36 | { 37 | get; 38 | set; 39 | } 40 | 41 | /// 42 | /// Gets the list of properties of a table entity (except for PartitionKey, RowKey and 43 | /// Timestamp) and their string values. 44 | /// 45 | public Dictionary Properties { get; } = new Dictionary(); 46 | 47 | /// 48 | /// Gets or sets the with the specified property. 49 | /// 50 | /// The name of the property. 51 | public string this[string property] 52 | { 53 | get 54 | { 55 | return this.Properties[property]; 56 | } 57 | 58 | set 59 | { 60 | this.Properties[property] = value; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Model/StorageAccountProperties.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Xml.Linq; 14 | using LINQPad.Extensibility.DataContext; 15 | using Madd0.AzureStorageDriver.Properties; 16 | #if NETCORE 17 | using Microsoft.Azure.Cosmos.Table; 18 | #else 19 | using Microsoft.Azure.Storage; 20 | using Microsoft.Azure.Storage.Auth; 21 | #endif 22 | 23 | /// 24 | /// Wrapper to expose typed properties over ConnectionInfo.DriverData. 25 | /// 26 | public class StorageAccountProperties 27 | { 28 | private readonly IConnectionInfo _connectionInfo; 29 | private readonly XElement _driverData; 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// The connection info. 35 | public StorageAccountProperties(IConnectionInfo connectionInfo) 36 | { 37 | this._connectionInfo = connectionInfo; 38 | this._driverData = connectionInfo.DriverData; 39 | } 40 | 41 | /// 42 | /// Gets or sets a value indicating whether this connection should be remembered. 43 | /// 44 | /// true if this connection should be remembered; otherwise, false. 45 | public bool Persist 46 | { 47 | get { return this._connectionInfo.Persist; } 48 | set { this._connectionInfo.Persist = value; } 49 | } 50 | 51 | /// 52 | /// Gets the display name of the connection. 53 | /// 54 | public string DisplayName 55 | { 56 | get 57 | { 58 | if (this.UseLocalStorage) 59 | { 60 | return Resources.LocalStorageDisplayName; 61 | } 62 | else 63 | { 64 | return this.AccountName; 65 | } 66 | } 67 | } 68 | 69 | /// 70 | /// Gets or sets a value indicating whether local storage is being used. 71 | /// 72 | /// true if local storage is used; otherwise, false. 73 | public bool UseLocalStorage 74 | { 75 | get 76 | { 77 | return (bool?)this._driverData.Element("UseLocalStorage") ?? false; 78 | } 79 | 80 | set 81 | { 82 | this._driverData.SetElementValue("UseLocalStorage", value); 83 | 84 | if (value) 85 | { 86 | this.ClearAccountNameAndKey(); 87 | } 88 | } 89 | } 90 | 91 | /// 92 | /// Gets or sets a value indicating whether to use HTTPS. 93 | /// 94 | /// true if HTTPS is used; otherwise, false. 95 | public bool UseHttps 96 | { 97 | get 98 | { 99 | return (bool?)this._driverData.Element("UseHttps") ?? false; 100 | } 101 | 102 | set 103 | { 104 | this._driverData.SetElementValue("UseHttps", value); 105 | } 106 | } 107 | 108 | /// 109 | /// Gets or sets the name of the storage account. 110 | /// 111 | public string AccountName 112 | { 113 | get { return (string)this._driverData.Element("AccountName") ?? string.Empty; } 114 | set { this._driverData.SetElementValue("AccountName", value); } 115 | } 116 | 117 | /// 118 | /// Gets or sets the key for the storage account. 119 | /// 120 | public string AccountKey 121 | { 122 | get 123 | { 124 | var encryptedKey = (string)this._driverData.Element("AccountKey") ?? string.Empty; 125 | return this._connectionInfo.Decrypt(encryptedKey); 126 | } 127 | 128 | set 129 | { 130 | var encryptedValue = this._connectionInfo.Encrypt(value); 131 | this._driverData.SetElementValue("AccountKey", encryptedValue); 132 | } 133 | } 134 | 135 | /// 136 | /// Gets or sets the number of rows to sample to determine a table's schema. 137 | /// 138 | public int NumberOfRows 139 | { 140 | get { return (int?)this._driverData.Element("NumberOfRows") ?? 1; } 141 | set { this._driverData.SetElementValue("NumberOfRows", value); } 142 | } 143 | 144 | /// 145 | /// Returns the maximum number of parallel model loading 146 | /// operations can occur when loading schema for the azure table storage tables 147 | /// 148 | public int ModelLoadMaxParallelism 149 | { 150 | get 151 | { 152 | return (int?)this._driverData.Element("ModelLoadMaxParallelism") ?? 153 | (System.Environment.ProcessorCount * 2); 154 | } 155 | set 156 | { 157 | this._driverData.SetElementValue("ModelLoadMaxParallelism", value); 158 | } 159 | } 160 | 161 | public IEnumerable Environments => AzureEnvironment.KnownEnvironments; 162 | 163 | public AzureEnvironment AzureEnvironment 164 | { 165 | get 166 | { 167 | var selected = (string)_driverData.Element("AzureEnvironment"); 168 | 169 | if (!string.IsNullOrEmpty(selected)) 170 | { 171 | return AzureEnvironment.Environments[selected]; 172 | } 173 | else 174 | { 175 | return AzureEnvironment.KnownEnvironments.First(); 176 | } 177 | } 178 | set 179 | { 180 | this._driverData.SetElementValue("AzureEnvironment", value.Name); 181 | } 182 | } 183 | 184 | /// 185 | /// Gets a instace for the current connection. 186 | /// 187 | /// A instace configured with the credentials 188 | /// of the current connection. 189 | public CloudStorageAccount GetStorageAccount() 190 | { 191 | if (this.UseLocalStorage) 192 | { 193 | return CloudStorageAccount.DevelopmentStorageAccount; 194 | } 195 | else 196 | { 197 | return new CloudStorageAccount( 198 | new StorageCredentials(this.AccountName, this.AccountKey), 199 | this.AzureEnvironment.StorageEndpointSuffix, 200 | this.UseHttps); 201 | } 202 | } 203 | 204 | /// 205 | /// Clears the account name and key. 206 | /// 207 | /// This method is called when local storage is used. 208 | private void ClearAccountNameAndKey() 209 | { 210 | var accountName = this._driverData.Element("AccountName"); 211 | var accountKey = this._driverData.Element("AccountKey"); 212 | 213 | if (null != accountName) 214 | { 215 | accountName.Remove(); 216 | } 217 | 218 | if (null != accountKey) 219 | { 220 | accountKey.Remove(); 221 | } 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Model/TableColumn.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.AzureStorageDriver 10 | { 11 | /// 12 | /// Holds information about a column of a table in Azure storage. 13 | /// 14 | public class TableColumn 15 | { 16 | /// 17 | /// Gets or sets the name of the property. 18 | /// 19 | public string Name 20 | { 21 | get; 22 | set; 23 | } 24 | 25 | /// 26 | /// Gets or sets the type name of the property. 27 | /// 28 | public string TypeName 29 | { 30 | get; 31 | set; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Properties/Exceptions.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.239 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Madd0.AzureStorageDriver.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Exceptions { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Exceptions() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Madd0.AzureStorageDriver.Properties.Exceptions", typeof(Exceptions).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Cannot compile typed context: {0} (line {1}). 65 | /// 66 | internal static string CannotCompileCode { 67 | get { 68 | return ResourceManager.GetString("CannotCompileCode", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Deserialized type '{0}' is not supported.. 74 | /// 75 | internal static string TypeNotSupported { 76 | get { 77 | return ResourceManager.GetString("TypeNotSupported", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Properties/Exceptions.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Cannot compile typed context: {0} (line {1}) 122 | 123 | 124 | Deserialized type '{0}' is not supported. 125 | 126 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.239 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Madd0.AzureStorageDriver.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Madd0.AzureStorageDriver.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to AzureTableStorage. 65 | /// 66 | internal static string AssemblyTitle { 67 | get { 68 | return ResourceManager.GetString("AssemblyTitle", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to 0.1.0.0. 74 | /// 75 | internal static string AssemblyVersion { 76 | get { 77 | return ResourceManager.GetString("AssemblyVersion", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Mauricio Diaz Orlich. 83 | /// 84 | internal static string AuthorName { 85 | get { 86 | return ResourceManager.GetString("AuthorName", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Azure Table Storage Driver. 92 | /// 93 | internal static string DriverName { 94 | get { 95 | return ResourceManager.GetString("DriverName", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to (Development). 101 | /// 102 | internal static string LocalStorageDisplayName { 103 | get { 104 | return ResourceManager.GetString("LocalStorageDisplayName", resourceCulture); 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Mauricio Diaz Orlich 122 | 123 | 124 | Azure Table Storage Driver 125 | 126 | 127 | (Development) 128 | 129 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/SchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2012 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | namespace Madd0.AzureStorageDriver 9 | { 10 | #if NETCORE 11 | using Microsoft.Azure.Cosmos.Table; 12 | #else 13 | using Microsoft.Azure.CosmosDB.Table; 14 | #endif 15 | 16 | using System; 17 | using System.Collections.Generic; 18 | using System.IO; 19 | using System.Linq; 20 | using System.Net; 21 | using System.Reflection; 22 | using System.Threading.Tasks; 23 | using LINQPad.Extensibility.DataContext; 24 | using Madd0.AzureStorageDriver.Properties; 25 | 26 | 27 | /// 28 | /// Provides the methods necessary to determining the storage account's schema and to building 29 | /// the typed data context . 30 | /// 31 | internal static class SchemaBuilder 32 | { 33 | // Names of columns that should be marked as table keys. 34 | private static readonly List KeyColumns = new List { "PartitionKey", "RowKey" }; 35 | 36 | /// 37 | /// Gets the schema and builds the assembly. 38 | /// 39 | /// The current configuration. 40 | /// The instace of the assembly being created. 41 | /// The namespace to be used in the generated code. 42 | /// Name of the type of the typed data context. 43 | /// A list of instaces that describes the current schema. 44 | public static List GetSchemaAndBuildAssembly(StorageAccountProperties properties, AssemblyName name, string @namepace, string typeName) 45 | { 46 | // Get the model from Azure storage 47 | var model = GetModel(properties); 48 | 49 | // Generate C# code 50 | var code = GenerateCode(typeName, @namepace, model); 51 | 52 | // And compile the code into the assembly 53 | BuildAssembly(name, code); 54 | 55 | // Generate the schema for LINQPad 56 | List schema = GetSchema(model); 57 | 58 | return schema; 59 | } 60 | 61 | /// 62 | /// Build a model of the current Azure storage account. This model will be used to generate 63 | /// the typed code as well as the schema needed by LINQPad. 64 | /// 65 | /// The current configuration. 66 | /// A list of instances that describe the current Azure 67 | /// storage model. 68 | private static IEnumerable GetModel(StorageAccountProperties properties) 69 | { 70 | // make sure that we can make at least ModelLoadMaxParallelism concurrent 71 | // cals to azure table storage 72 | ServicePointManager.DefaultConnectionLimit = properties.ModelLoadMaxParallelism; 73 | 74 | var tableClient = properties.GetStorageAccount().CreateCloudTableClient(); 75 | 76 | // First get a list of all tables 77 | var model = (from tableName in tableClient.ListTables() 78 | select new CloudTable 79 | { 80 | Name = tableName.Name 81 | }).ToList(); 82 | 83 | var options = new ParallelOptions() 84 | { 85 | MaxDegreeOfParallelism = properties.ModelLoadMaxParallelism 86 | }; 87 | 88 | Parallel.ForEach(model, options, table => 89 | { 90 | var threadTableClient = properties.GetStorageAccount().CreateCloudTableClient(); 91 | 92 | var tableColumns = threadTableClient.GetTableReference(table.Name).ExecuteQuery(new TableQuery().Take(properties.NumberOfRows)) 93 | .SelectMany(row => row.Properties) 94 | .GroupBy(column => column.Key) 95 | .Select(grp => new TableColumn 96 | { 97 | Name = grp.Key, 98 | TypeName = GetType(grp.First().Value.PropertyType) 99 | }); 100 | 101 | var baseColumns = new List 102 | { 103 | new TableColumn { Name = "PartitionKey", TypeName = GetType(EdmType.String) }, 104 | new TableColumn { Name = "RowKey", TypeName = GetType(EdmType.String) }, 105 | new TableColumn { Name = "Timestamp", TypeName = GetType(EdmType.DateTime) }, 106 | new TableColumn { Name = "ETag", TypeName = GetType(EdmType.String) } 107 | }; 108 | 109 | table.Columns = tableColumns.Concat(baseColumns).ToArray(); 110 | }); 111 | 112 | return model; 113 | } 114 | 115 | /// 116 | /// Generates the code to build the typed data context. 117 | /// 118 | /// Name of the type. 119 | /// The namespace. 120 | /// The model. 121 | /// The code to be compiled as a string. 122 | private static string GenerateCode(string typeName, string @namespace, IEnumerable model) 123 | { 124 | // We use a T4-generated class as the template 125 | var codeGenerator = new DataContextTemplate 126 | { 127 | Namespace = @namespace, 128 | TypeName = typeName, 129 | Tables = model 130 | }; 131 | 132 | // As a workaround for compiling issues when referencing the driver DLL itself to 133 | // access the base ExtendedTableQuery class, the code will be used instead in the 134 | // dynamic assembly. 135 | var baseClass = ReadEmbeddedBaseClassCode(); 136 | 137 | var sourceCode = baseClass + codeGenerator.TransformText(); 138 | LINQPad.Util.Break(); 139 | 140 | return sourceCode; 141 | } 142 | 143 | private static string ReadEmbeddedBaseClassCode() 144 | { 145 | var assembly = Assembly.GetExecutingAssembly(); 146 | var resourceName = "Madd0.AzureStorageDriver.ExtendedTableQuery.cs"; 147 | 148 | using var reader = new StreamReader(assembly.GetManifestResourceStream(resourceName)); 149 | 150 | return reader.ReadToEnd(); 151 | } 152 | 153 | /// 154 | /// Builds the assembly described by the . 155 | /// 156 | /// The instace of the assembly being created. 157 | /// The code of the typed data context. 158 | private static void BuildAssembly(AssemblyName name, string code) 159 | { 160 | ICompiler compiler; 161 | 162 | var dependencies = new List 163 | { 164 | typeof(TableQuery).Assembly.Location 165 | }; 166 | #if NETCORE 167 | compiler = new RoslynCompiler(); 168 | #else 169 | compiler = new CodeDomCompiler(); 170 | dependencies.Add("System.dll"); 171 | dependencies.Add("System.Core.dll"); 172 | dependencies.Add("System.Xml.dll"); 173 | dependencies.Add(typeof(Microsoft.Azure.Storage.OperationContext).Assembly.Location); 174 | #endif 175 | compiler.Compile(code, name, dependencies); 176 | } 177 | 178 | /// 179 | /// Transforms the model based on instances into a schema based on 180 | /// instances for LINQPad. 181 | /// 182 | /// The model. 183 | /// A schema for LINQPad. 184 | private static List GetSchema(IEnumerable model) 185 | { 186 | return (from table in model 187 | select new ExplorerItem(table.Name, ExplorerItemKind.QueryableObject, ExplorerIcon.Table) 188 | { 189 | Children = (from column in table.Columns 190 | select new ExplorerItem(column.Name + " (" + column.TypeName + ")", ExplorerItemKind.Property, ExplorerIcon.Column) 191 | { 192 | Icon = KeyColumns.Contains(column.Name) ? ExplorerIcon.Key : ExplorerIcon.Column, 193 | DragText = column.Name 194 | }).ToList(), 195 | DragText = table.Name, 196 | IsEnumerable = true 197 | }).ToList(); 198 | } 199 | 200 | /// 201 | /// Gets the C# type equivalent of an entity data model (Edm) type. 202 | /// 203 | /// The Edm type. 204 | /// The C# type. 205 | private static string GetType(EdmType type) 206 | { 207 | return type switch 208 | { 209 | EdmType.Binary => "byte[]", 210 | 211 | EdmType.Boolean => "bool?", 212 | 213 | EdmType.DateTime => "DateTime?", 214 | 215 | EdmType.Double => "double?", 216 | 217 | EdmType.Guid => "Guid?", 218 | 219 | EdmType.Int32 => "int?", 220 | 221 | EdmType.Int64 => "long?", 222 | 223 | EdmType.String => "string", 224 | 225 | _ => throw new NotSupportedException(string.Format(Exceptions.TypeNotSupported, type)), 226 | }; 227 | } 228 | } 229 | } -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/UserQuery/ExtendedTableQuery.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (c) 2014 Mauricio DIAZ ORLICH. 4 | // Code licensed under the MIT X11 license. 5 | // 6 | // Mauricio DIAZ ORLICH 7 | //----------------------------------------------------------------------- 8 | 9 | namespace Madd0.UserQuery 10 | { 11 | #if NETCORE 12 | using Microsoft.Azure.Cosmos.Table; 13 | #else 14 | using Microsoft.Azure.CosmosDB.Table; 15 | using Microsoft.Azure.Storage; 16 | #endif 17 | using System.Collections; 18 | using System.Collections.Generic; 19 | using System.IO; 20 | using System.Linq; 21 | 22 | public class ExtendedTableQuery 23 | { 24 | 25 | public TextWriter QueryWriter 26 | { 27 | get; 28 | set; 29 | } 30 | 31 | } 32 | public class ExtendedTableQuery : ExtendedTableQuery, IQueryable 33 | where TElement : ITableEntity, new() 34 | { 35 | private readonly TableQuery _query; 36 | 37 | public ExtendedTableQuery(CloudTable table) 38 | { 39 | this.CloudTable = table; 40 | _query = table.CreateQuery(); 41 | } 42 | 43 | #if !NETCORE 44 | public ICancellableAsyncResult BeginExecuteSegmented(TableContinuationToken currentToken, TableRequestOptions requestOptions, OperationContext operationContext, System.AsyncCallback callback, object state) 45 | { 46 | return _query.BeginExecuteSegmented(currentToken, requestOptions, operationContext, callback, state); 47 | } 48 | 49 | public ICancellableAsyncResult BeginExecuteSegmented(TableContinuationToken currentToken, System.AsyncCallback callback, object state) 50 | { 51 | return _query.BeginExecuteSegmented(currentToken, callback, state); 52 | } 53 | 54 | public TableQuerySegment EndExecuteSegmented(System.IAsyncResult asyncResult) 55 | { 56 | return _query.EndExecuteSegmented(asyncResult); 57 | } 58 | #endif 59 | 60 | public System.Collections.Generic.IEnumerable Execute(TableRequestOptions requestOptions = null, OperationContext operationContext = null) 61 | { 62 | return _query.Execute(requestOptions, operationContext); 63 | } 64 | 65 | public TableQuerySegment ExecuteSegmented(TableContinuationToken continuationToken, TableRequestOptions requestOptions = null, OperationContext operationContext = null) 66 | { 67 | return _query.ExecuteSegmented(continuationToken, requestOptions, operationContext); 68 | } 69 | 70 | public System.Threading.Tasks.Task> ExecuteSegmentedAsync(TableContinuationToken currentToken, TableRequestOptions requestOptions, OperationContext operationContext, System.Threading.CancellationToken cancellationToken) 71 | { 72 | return _query.ExecuteSegmentedAsync(currentToken, requestOptions, operationContext, cancellationToken); 73 | } 74 | 75 | public System.Threading.Tasks.Task> ExecuteSegmentedAsync(TableContinuationToken currentToken, TableRequestOptions requestOptions, OperationContext operationContext) 76 | { 77 | return _query.ExecuteSegmentedAsync(currentToken, requestOptions, operationContext); 78 | } 79 | 80 | public System.Threading.Tasks.Task> ExecuteSegmentedAsync(TableContinuationToken currentToken, System.Threading.CancellationToken cancellationToken) 81 | { 82 | return _query.ExecuteSegmentedAsync(currentToken, cancellationToken); 83 | } 84 | 85 | public System.Threading.Tasks.Task> ExecuteSegmentedAsync(TableContinuationToken currentToken) 86 | { 87 | return _query.ExecuteSegmentedAsync(currentToken); 88 | } 89 | 90 | public System.Collections.Generic.IEnumerator GetEnumerator() 91 | { 92 | return _query.GetEnumerator(); 93 | } 94 | 95 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 96 | { 97 | return this.GetEnumerator(); 98 | } 99 | 100 | public TableQuery Select(System.Collections.Generic.IList columns) 101 | { 102 | return _query.Select(columns); 103 | } 104 | 105 | public TableQuery Take(int? take) 106 | { 107 | return _query.Take(take); 108 | } 109 | 110 | public TableQuery Where(string filter) 111 | { 112 | return _query.Where(filter); 113 | } 114 | 115 | public System.Type ElementType 116 | { 117 | get 118 | { 119 | return _query.ElementType; 120 | } 121 | } 122 | 123 | public System.Linq.Expressions.Expression Expression 124 | { 125 | get 126 | { 127 | return _query.Expression; 128 | } 129 | } 130 | 131 | public string FilterString 132 | { 133 | set 134 | { 135 | _query.FilterString = value; 136 | } 137 | get 138 | { 139 | return _query.FilterString; 140 | } 141 | } 142 | 143 | public System.Linq.IQueryProvider Provider 144 | { 145 | get 146 | { 147 | return _query.Provider; 148 | } 149 | } 150 | 151 | public System.Collections.Generic.IList SelectColumns 152 | { 153 | set 154 | { 155 | _query.SelectColumns = value; 156 | } 157 | get 158 | { 159 | return _query.SelectColumns; 160 | } 161 | } 162 | 163 | public int? TakeCount 164 | { 165 | set 166 | { 167 | _query.TakeCount = value; 168 | } 169 | get 170 | { 171 | return _query.TakeCount; 172 | } 173 | } 174 | 175 | public CloudTable CloudTable { get; private set; } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Madd0.AzureStorageDriver/header.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | Madd0.AzureStorageDriver.dll 4 | https://github.com/madd0/AzureStorageDriver 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README # 2 | 3 | This is a plug-in for [LINQPad](http://www.linqpad.net) that enables the 4 | execution of queries against [Azure Table Storage](https://docs.microsoft.com/en-us/azure/cosmos-db/table-storage-overview). 5 | 6 | It allows you to add storage accounts as connections in LINQPad. 7 | It will list all tables for each account and all columns in each table. 8 | Since Azure tables can have mixed schemas, a property was added in version 1.1.0 9 | allowing users to specify the number of rows that should be scanned to determine 10 | the schema of each table. For the same reason, any value types are made 11 | `Nullable<>` since it cannot be guaranteed that they will have a value 12 | for a given row. 13 | 14 | [![Nuget](https://img.shields.io/nuget/v/Madd0.AzureStorageDriver?logo=nuget)](https://www.nuget.org/packages/Madd0.AzureStorageDriver/) [![Build Status](https://dev.azure.com/madd0/AzureStorageDriver/_apis/build/status/madd0.AzureStorageDriver?branchName=master)](https://dev.azure.com/madd0/AzureStorageDriver/_build/latest?definitionId=4&branchName=master) 15 | 16 | ## Issues and Feature requests ## 17 | 18 | Issues and feature requests can be made in the projects 19 | [issues page on GitHub](https://github.com/madd0/AzureStorageDriver/issues). 20 | 21 | ## Versions ## 22 | 23 | ### v2.1.2 - 2022-11-13 ### 24 | 25 | * Updated dependency versions: 26 | * LINQPad.Reference: 1.1.0 -> 1.3.0 27 | * SonarAnalyzer.CSharp: 8.0.0.9566 -> 8.48.0.56517 28 | * Microsoft.CodeAnalysis.CSharp: 3.3.1 -> 3.11.0 29 | 30 | * Added explicit reference to Newtonsoft.Json to work around issue [#22][11] 31 | ### v2.1.1 - 2020-02-04 ### 32 | 33 | * Workaround for problem described in [#20][10] 34 | * The .NET Core 3.1 driver was getting .NET Core 3.0 references, which prevented it from compiling the generated code because 35 | the driver itself was a reference and therefore needed 3.1 references. 36 | * As a workaround, the driver is no longer a required reference for the generated code, which is compatible with either 37 | .NET Core 3.0 or 3.1, so no matter what runtime LinqPad provides, the code should compile. 38 | 39 | ### v2.1.0 - 2020-01-05 ### 40 | 41 | Happy new year! After last month's update, I decided to work on some really 42 | old pending PRs, do some cleanup and tackle [#19][7] which came in on the first day of the year. 43 | 44 | * Integrate PR [#8: add support for china azure(code name:mooncake)][8] 45 | * Adds support for China, US Government and German "clouds." 46 | * Found by expanding the _Advanced_ section in the connection properties dialog. 47 | * Integrate PR [#12: Enable parallel schema loading ][9] 48 | * Speeds up loading of storage accounts with multiple tables. 49 | * Max number of parallel queries can be set in the _Advanced_ section in the connection properties dialog. 50 | * Target .NET Core 3.1 51 | * The Nuget package now includes .NET 4.6, .NET Core 3.0 _and_ .NET Core 3.1 versions. 52 | * The .NET Core 3.1 version of the DLL should prevent issues such as described in [#19: When .NET Core 3.1 installed, "Cannot compile typed context" error][7] 53 | 54 | ### v2.0.0 - 2019-12-01 ### 55 | 56 | It took a while for a new version to come out, but with [LINQPad 6][3] 57 | and big changes in the Azure SDK libraries, I thought it was time. 58 | And, although functionally not much has changed, I decided that a new 59 | distribution channel and total change in the underlying SDK, 60 | deserved a new major version. 61 | 62 | * **New:** Available as [a Nuget package][4] for LINQPad 6. 63 | * Switch to _Azure Cosmos DB Table API_ 64 | * LINQPad 6 plugin uses [Microsoft.Azure.Cosmos.Table 1.0.5][5] 65 | * LINQPad 5 plugin uses [Microsoft.Azure.CosmosDB.Table 2.1.2][6] 66 | 67 | 68 | _Note: the Azure Storage SDK used by the LINQPad 5 version is in maintenance mode 69 | and it will be deprecated soon according to Microsoft. However, since LINQPad 5 70 | plugins target .NET Framework 4.6, the switch to the package used by the LINQPad 6 71 | plugin is not possible._ 72 | 73 | ### v1.1.0 - 2014-08-20 ### 74 | 75 | * Uses latest version of [Azure Storage Client Library](https://github.com/Azure/azure-storage-net/) (4.2.0) 76 | * Provides a solution to issue [#1][2] by adding a configuration parameter that allows users to specify 77 | the number of rows to be scanned to determine the schema of a table (100 by default). 78 | 79 | ### v1.0.1 - 2013-08-15 ### 80 | 81 | Corrects issue [#4][1]. 82 | 83 | ### v1.0.0-beta - 2010-01-08 ### 84 | 85 | This is the first public release. Needs real-world testing. 86 | 87 | [1]: https://github.com/madd0/AzureStorageDriver/issues/4 88 | [2]: https://github.com/madd0/AzureStorageDriver/issues/1 89 | [3]: https://www.linqpad.net/LINQPad6.aspx 90 | [4]: https://www.nuget.org/packages/Madd0.AzureStorageDriver/ 91 | [5]: https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Table 92 | [6]: https://www.nuget.org/packages/Microsoft.Azure.CosmosDB.Table 93 | [7]: https://github.com/madd0/AzureStorageDriver/issues/19 94 | [8]: https://github.com/madd0/AzureStorageDriver/pull/8 95 | [9]: https://github.com/madd0/AzureStorageDriver/pull/12 96 | [10]: https://github.com/madd0/AzureStorageDriver/issues/20 97 | [11]: https://github.com/madd0/AzureStorageDriver/issues/22 98 | -------------------------------------------------------------------------------- /Settings.XamlStyler: -------------------------------------------------------------------------------- 1 | { 2 | "KeepFirstAttributeOnSameLine": true, 3 | "ThicknessSeparator": 1 4 | } 5 | --------------------------------------------------------------------------------