[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome:http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Don't use tabs for indentation.\n[*]\nindent_style = space\n# (Please don't specify an indent_size here; that has too many unintended consequences.)\n\n# Code files\n[*.{cs,csx,vb,vbx}]\nindent_size = 4\ninsert_final_newline = true\ncharset = utf-8-bom\n\n# Xml project files\n[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]\nindent_size = 2\n\n# Xml config files\n[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]\nindent_size = 2\n\n# JSON files\n[*.json]\nindent_size = 2\n\n# Dotnet code style settings:\n[*.{cs,vb}]\n# Sort using and Import directives with System.* appearing first\ndotnet_sort_system_directives_first = true\n# Avoid \"this.\" and \"Me.\" if not necessary\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_event = false:suggestion\n\n# Use language keywords instead of framework type names for type references\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\ndotnet_style_predefined_type_for_member_access = true:suggestion\n\n# Suggest more modern language features when available\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_explicit_tuple_names = true:suggestion\n\n# CSharp code style settings:\n[*.cs]\n# Prefer \"var\" everywhere\n#csharp_style_var_for_built_in_types = true:suggestion\n#csharp_style_var_when_type_is_apparent = false:suggestion\n#csharp_style_var_elsewhere = true:suggestion\n\n# Prefer method-like constructs to have a expression-body\ncsharp_style_expression_bodied_methods = true:none\ncsharp_style_expression_bodied_constructors = true:none\ncsharp_style_expression_bodied_operators = true:none\n\n# Prefer property-like constructs to have an expression-body\ncsharp_style_expression_bodied_properties = true:none\ncsharp_style_expression_bodied_indexers = true:none\ncsharp_style_expression_bodied_accessors = true:none\n\n# Suggest more modern language features when available\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\ncsharp_style_inlined_variable_declaration = true:suggestion\ncsharp_style_throw_expression = true:suggestion\ncsharp_style_conditional_delegate_call = true:suggestion\n\n# Newline settings\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_before_else = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_members_in_anonymous_types = true"
  },
  {
    "path": ".gitattributes",
    "content": "﻿* text=auto\n\n*.doc  diff=astextplain\n*.DOC\tdiff=astextplain\n*.docx\tdiff=astextplain\n*.DOCX\tdiff=astextplain\n*.dot\tdiff=astextplain\n*.DOT\tdiff=astextplain\n*.pdf\tdiff=astextplain\n*.PDF\tdiff=astextplain\n*.rtf\tdiff=astextplain\n*.RTF\tdiff=astextplain\n\n*.jpg binary\n*.png binary\n*.gif binary\n\n*.cs -text diff=csharp \n*.vb -text\n*.c -text\n*.cpp -text\n*.cxx -text\n*.h -text\n*.hxx -text\n*.py -text\n*.rb -text\n*.java -text\n*.html -text\n*.htm -text\n*.css -text\n*.scss -text\n*.sass -text\n*.less -text\n*.js -text\n*.lisp -text\n*.clj -text\n*.sql -text\n*.php -text\n*.lua -text\n*.m -text\n*.asm -text\n*.erl -text\n*.fs -text\n*.fsx -text\n*.hs -text\n\n*.csproj -text merge=union \n*.vbproj -text merge=union \n*.fsproj -text merge=union \n*.dbproj -text merge=union \n*.sln -text merge=union \n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Main Build\n\non:\n  pull_request:\n  push:\n    branches:\n    - main\n    paths:\n    - '*'\n    - '!/docs/*' # Don't run workflow when files are only in the /docs directory\n\njobs:\n  vm-job:\n    name: Ubuntu\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres\n        ports:\n        - 5432/tcp\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_DB: test\n        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\n      sqlserver:\n        image: mcr.microsoft.com/mssql/server:2019-latest\n        ports:\n        - 1433/tcp\n        env:\n          ACCEPT_EULA: Y\n          SA_PASSWORD: \"Password.\"\n      mysql:\n        image: mysql\n        ports:\n        - 3306/tcp\n        env:\n          MYSQL_ROOT_PASSWORD: root\n          MYSQL_DATABASE: test\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v1\n    - name: .NET Build\n      run: dotnet build Build.csproj -c Release /p:CI=true\n    - name: Dapper.Contrib Tests\n      run: dotnet test tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj -c Release --logger GitHubActions /p:CI=true\n      env:\n        MySqlConnectionString: Server=localhost;Port=${{ job.services.mysql.ports[3306] }};Uid=root;Pwd=root;Database=test;Allow User Variables=true\n        OLEDBConnectionString: Provider=SQLOLEDB;Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;\n        PostgesConnectionString: Server=localhost;Port=${{ job.services.postgres.ports[5432] }};Database=test;User Id=postgres;Password=postgres;\n        SqlServerConnectionString: Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;\n    - name: .NET Lib Pack\n      run: dotnet pack Build.csproj --no-build -c Release /p:PackageOutputPath=%CD%\\.nupkgs /p:CI=true\n"
  },
  {
    "path": ".gitignore",
    "content": "/*.suo\n.vs/\n.vscode/\nbin/\nobj/\n/*.user\n_Resharper*\n.hgtags\nNuGet.exe\n*.user\n*.nupkg\n.nupkgs/\n.docstats\n*.ide/\n*.lock.json\n*.coverage\nTest.DB.*\nTestResults/\n.dotnet/*\nBenchmarkDotNet.Artifacts/\n.idea/\n.DS_Store"
  },
  {
    "path": "Build.csproj",
    "content": "<Project Sdk=\"Microsoft.Build.Traversal/2.0.24\">\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"*/*.csproj\" />\r\n    <ProjectReference Include=\"tests/**/*.csproj\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "Dapper.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.28917.182\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{A34907DF-958A-4E4C-8491-84CF303FD13E}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\t.editorconfig = .editorconfig\r\n\t\tappveyor.yml = appveyor.yml\r\n\t\tbuild.ps1 = build.ps1\r\n\t\tDirectory.Build.props = Directory.Build.props\r\n\t\tdocs\\index.md = docs\\index.md\r\n\t\tLicense.txt = License.txt\r\n\t\tnuget.config = nuget.config\r\n\t\tReadme.md = Readme.md\r\n\t\tversion.json = version.json\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Dapper.Contrib\", \"Dapper.Contrib\\Dapper.Contrib.csproj\", \"{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Dapper.Tests.Contrib\", \"tests\\Dapper.Tests.Contrib\\Dapper.Tests.Contrib.csproj\", \"{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"src\", \"src\", \"{4E956F6B-6BD8-46F5-BC85-49292FF8F9AB}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tDirectory.Build.props = Directory.Build.props\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"tests\", \"tests\", \"{568BD46C-1C65-4D44-870C-12CD72563262}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\ttests\\Directory.Build.props = tests\\Directory.Build.props\r\n\t\ttests\\Directory.Build.targets = tests\\Directory.Build.targets\r\n\t\ttests\\docker-compose.yml = tests\\docker-compose.yml\r\n\tEndProjectSection\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(NestedProjects) = preSolution\r\n\t\t{4E409F8F-CFBB-4332-8B0A-FD5A283051FD} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB}\r\n\t\t{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A} = {568BD46C-1C65-4D44-870C-12CD72563262}\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {928A4226-96F3-409A-8A83-9E7444488710}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n  <PropertyGroup>\n    <Copyright>2019 Stack Exchange, Inc.</Copyright>\n\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <AssemblyOriginatorKeyFile>../Dapper.snk</AssemblyOriginatorKeyFile>\n\n    <PackageId>$(AssemblyName)</PackageId>\n    <PackageReleaseNotes>https://dapperlib.github.io/Dapper.Contrib/</PackageReleaseNotes>\n    <PackageProjectUrl>https://github.com/DapperLib/Dapper.Contrib</PackageProjectUrl>\n    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>\n    <PackageIcon>Dapper.png</PackageIcon>\n    <RepositoryType>git</RepositoryType>\n    <RepositoryUrl>https://github.com/DapperLib/Dapper.Contrib</RepositoryUrl>\n    <Deterministic>false</Deterministic>\n    <NoWarn>$(NOWARN);IDE0056;IDE0057;IDE0079</NoWarn>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>embedded</DebugType>\n    <DefaultLanguage>en-US</DefaultLanguage>\n    <IncludeSymbols>false</IncludeSymbols>\n\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    \n    <LangVersion>9.0</LangVersion>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(Configuration)' == 'Release'\">\n    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>\n    <Deterministic>true</Deterministic>\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n  </PropertyGroup>\n  <ItemGroup Condition=\"'$(Configuration)' == 'Release' and '$(SourceRoot)'==''\">\n    <SourceRoot Include=\"$(MSBuildThisFileDirectory)/\"/>\n  </ItemGroup>\n  \n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.0\" PrivateAssets=\"All\" IncludeAssets=\"runtime; build; native; contentfiles; analyzers\" />\n    <PackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"1.0.0\" PrivateAssets=\"All\"/>\n    <PackageReference Include=\"Nerdbank.GitVersioning\" Version=\"3.3.37\" PrivateAssets=\"all\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Directory.Build.targets",
    "content": "<Project>\n  <!-- workaround for deterministic builds; see https://github.com/clairernovotny/DeterministicBuilds -->\n  <PropertyGroup>\n    <TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>\n  </PropertyGroup>\n  <ItemGroup>\n    <EmbeddedFiles Include=\"$(GeneratedAssemblyInfoFile)\"/>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "License.txt",
    "content": "The Dapper.Contrib library and tools are licenced under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0"
  },
  {
    "path": "Readme.md",
    "content": "Dapper.Contrib - a simple object mapper for .Net\n========================================\n[![Build status](https://ci.appveyor.com/api/projects/status/1w448i6nfxd14w75?svg=true)](https://ci.appveyor.com/project/StackExchange/dapper-contrib)\n\nRelease Notes\n-------------\nLocated at [dapperlib.github.io/Dapper.Contrib](https://dapperlib.github.io/Dapper.Contrib/)\n\nPackages\n--------\n\nMyGet Pre-release feed: https://www.myget.org/gallery/dapper\n\n| Package | NuGet Stable | NuGet Pre-release | Downloads | MyGet |\n| ------- | ------------ | ----------------- | --------- | ----- |\n| [Dapper.Contrib](https://www.nuget.org/packages/Dapper.Contrib/) | [![Dapper.Contrib](https://img.shields.io/nuget/v/Dapper.Contrib.svg)](https://www.nuget.org/packages/Dapper.Contrib/) | [![Dapper.Contrib](https://img.shields.io/nuget/vpre/Dapper.Contrib.svg)](https://www.nuget.org/packages/Dapper.Contrib/) | [![Dapper.Contrib](https://img.shields.io/nuget/dt/Dapper.Contrib.svg)](https://www.nuget.org/packages/Dapper.Contrib/) | [![Dapper.Contrib MyGet](https://img.shields.io/myget/dapper/vpre/Dapper.Contrib.svg)](https://www.myget.org/feed/dapper/package/nuget/Dapper.Contrib) |\n\nFeatures\n--------\n\nDapper.Contrib contains a number of helper methods for inserting, getting,\nupdating and deleting records.\n\nThe full list of extension methods in Dapper.Contrib right now are:\n\n```csharp\nT Get<T>(id);\nIEnumerable<T> GetAll<T>();\nint Insert<T>(T obj);\nint Insert<T>(Enumerable<T> list);\nbool Update<T>(T obj);\nbool Update<T>(Enumerable<T> list);\nbool Delete<T>(T obj);\nbool Delete<T>(Enumerable<T> list);\nbool DeleteAll<T>();\n```\n\nFor these extensions to work, the entity in question _MUST_ have a\nkey property. Dapper will automatically use a property named \"`id`\" \n(case-insensitive) as the key property, if one is present.\n\n```csharp\npublic class Car\n{\n    public int Id { get; set; } // Works by convention\n    public string Name { get; set; }\n}\n```\n\nIf the entity doesn't follow this convention, decorate \na specific property with a `[Key]` or `[ExplicitKey]` attribute.\n\n```csharp\npublic class User\n{\n    [Key]\n    int TheId { get; set; }\n    string Name { get; set; }\n    int Age { get; set; }\n}\n```\n\n`[Key]` should be used for database-generated keys (e.g. autoincrement columns), \nwhile `[ExplicitKey]` should be used for explicit keys generated in code.\n\n`Get` methods\n-------\n\nGet one specific entity based on id\n\n```csharp\nvar car = connection.Get<Car>(1);\n```\n\nor a list of all entities in the table.\n\n```csharp\nvar cars = connection.GetAll<Car>();\n```\n\n`Insert` methods\n-------\n\nInsert one entity\n\n```csharp\nconnection.Insert(new Car { Name = \"Volvo\" });\n```\n\nor a list of entities.\n\n```csharp\nconnection.Insert(cars);\n```\n\n\n\n`Update` methods\n-------\nUpdate one specific entity\n\n```csharp\nconnection.Update(new Car() { Id = 1, Name = \"Saab\" });\n```\n\nor update a list of entities.\n\n```csharp\nconnection.Update(cars);\n```\n\n`Delete` methods\n-------\nDelete an entity by the specified `[Key]` property\n\n```csharp\nconnection.Delete(new Car() { Id = 1 });\n```\n\na list of entities\n\n```csharp\nconnection.Delete(cars);\n```\n\nor _ALL_ entities in the table.\n\n```csharp\nconnection.DeleteAll<Car>();\n```\n\nSpecial Attributes\n----------\nDapper.Contrib makes use of some optional attributes:\n\n* `[Table(\"Tablename\")]` - use another table name instead of the (by default pluralized) name of the class\n\n    ```csharp\n    [Table (\"emps\")]\n    public class Employee\n    {\n        public int Id { get; set; }\n        public string Name { get; set; }\n    }\n    ```\n* `[Key]` - this property represents a database-generated identity/key\n    \n    ```csharp\n    public class Employee\n    {\n        [Key]\n        public int EmployeeId { get; set; }\n        public string Name { get; set; }\n    }\n    ```\n* `[ExplicitKey]` - this property represents an explicit identity/key which is \n  *not* automatically generated by the database \n\n    ```csharp\n    public class Employee\n    {\n        [ExplicitKey]\n        public Guid EmployeeId { get; set; }\n        public string Name { get; set; }\n    }\n    ```\n* `[Write(true/false)]` -  this property is (not) writeable\n* `[Computed]` - this property is computed and should not be part of updates\n\nLimitations and caveats\n-------\n\n### SQLite\n\n`SQLiteConnection` exposes an `Update` event that clashes with the `Update`\nextension provided by Dapper.Contrib. There are 2 ways to deal with this.\n\n1. Call the `Update` method explicitly from `SqlMapperExtensions`\n\n    ```Csharp\n    SqlMapperExtensions.Update(_conn, new Employee { Id = 1, Name = \"Mercedes\" });\n    ```\n2. Make the method signature unique by passing a type parameter to `Update`\n\n    ```Csharp\n    connection.Update<Car>(new Car() { Id = 1, Name = \"Maruti\" });\n    ```\n"
  },
  {
    "path": "appveyor.yml",
    "content": "image: Visual Studio 2019\n\nskip_branch_with_pr: true\nskip_tags: true\nskip_commits:\n  files:\n    - '**/*.md'\n\nenvironment:\n  Appveyor: true\n  # Postgres\n  POSTGRES_PATH: C:\\Program Files\\PostgreSQL\\9.6\n  PGUSER: postgres\n  PGPASSWORD: Password12!\n  POSTGRES_ENV_POSTGRES_USER: postgres\n  POSTGRES_ENV_POSTGRES_PASSWORD: Password12!\n  POSTGRES_ENV_POSTGRES_DB: test\n  # MySQL\n  MYSQL_PATH: C:\\Program Files\\MySql\\MySQL Server 5.7\n  MYSQL_PWD: Password12!\n  MYSQL_ENV_MYSQL_USER: root\n  MYSQL_ENV_MYSQL_PASSWORD: Password12!\n  MYSQL_ENV_MYSQL_DATABASE: test\n  # Connection strings for tests:\n  MySqlConnectionString: Server=localhost;Database=test;Uid=root;Pwd=Password12!\n  OLEDBConnectionString: Provider=SQLOLEDB;Data Source=(local)\\SQL2019;Initial Catalog=tempdb;User Id=sa;Password=Password12!\n  PostgesConnectionString: Server=localhost;Port=5432;User Id=postgres;Password=Password12!;Database=test\n  SqlServerConnectionString: Server=(local)\\SQL2019;Database=tempdb;User ID=sa;Password=Password12!\n\nservices:\n  - mysql\n  - postgresql\n\ninit:\n  - git config --global core.autocrlf input\n  - SET PATH=%POSTGRES_PATH%\\bin;%MYSQL_PATH%\\bin;%PATH%\n  - net start MSSQL$SQL2019\n\nnuget:\n  disable_publish_on_pr: true\n\nbuild_script:\n  # Postgres\n  - createdb test\n  # MySQL\n  - mysql -e \"create database test;\" --user=root\n  # Our stuff\n  - ps: .\\build.ps1 -PullRequestNumber \"$env:APPVEYOR_PULL_REQUEST_NUMBER\" -CreatePackages $true\n\ntest: off\nartifacts:\n  - path: .\\.nupkgs\\*.nupkg\n  \ndeploy:\n- provider: NuGet\n  server: https://www.myget.org/F/stackoverflow/api/v2\n  on:\n    branch: main  \n  api_key:\n    secure: P/UHxq2DEs0GI1SoDXDesHjRVsSVgdywz5vmsnhFQQY5aJgO3kP+QfhwfhXz19Rw\n  symbol_server: https://www.myget.org/F/stackoverflow/symbols/api/v2/package\n- provider: NuGet\n  server: https://www.myget.org/F/dapper/api/v2\n  on:\n    branch: main  \n  api_key:\n    secure: PV7ERAltWWLhy7AT2h+Vb5c1BM9/WFgvggb+rKyQ8hDg3fYqpZauYdidOOgt2lp4\n  symbol_server: https://www.myget.org/F/dapper/api/v2/package"
  },
  {
    "path": "build.cmd",
    "content": "@ECHO OFF\nPowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command \"[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE\""
  },
  {
    "path": "build.ps1",
    "content": "[CmdletBinding(PositionalBinding=$false)]\nparam(\n    [bool] $CreatePackages,\n    [bool] $RunTests = $true,\n    [string] $PullRequestNumber\n)\n\nWrite-Host \"Run Parameters:\" -ForegroundColor Cyan\nWrite-Host \"  CreatePackages: $CreatePackages\"\nWrite-Host \"  RunTests: $RunTests\"\nWrite-Host \"  dotnet --version:\" (dotnet --version)\n\n$packageOutputFolder = \"$PSScriptRoot\\.nupkgs\"\n\nif ($PullRequestNumber) {\n    Write-Host \"Building for a pull request (#$PullRequestNumber), skipping packaging.\" -ForegroundColor Yellow\n    $CreatePackages = $false\n}\n\nWrite-Host \"Building all projects (Build.csproj traversal)...\" -ForegroundColor \"Magenta\"\ndotnet build \".\\Build.csproj\" -c Release /p:CI=true\nWrite-Host \"Done building.\" -ForegroundColor \"Green\"\n\nif ($RunTests) {\n    Write-Host \"Running tests: Build.csproj traversal (all frameworks)\" -ForegroundColor \"Magenta\"\n    dotnet test \".\\Build.csproj\" -c Release --no-build\n    if ($LastExitCode -ne 0) {\n        Write-Host \"Error with tests, aborting build.\" -Foreground \"Red\"\n        Exit 1\n    }\n    Write-Host \"Tests passed!\" -ForegroundColor \"Green\"\n}\n\nif ($CreatePackages) {\n    New-Item -ItemType Directory -Path $packageOutputFolder -Force | Out-Null\n    Write-Host \"Clearing existing $packageOutputFolder...\" -NoNewline\n    Get-ChildItem $packageOutputFolder | Remove-Item\n    Write-Host \"done.\" -ForegroundColor \"Green\"\n\n    Write-Host \"Building all packages\" -ForegroundColor \"Green\"\n    dotnet pack \".\\Build.csproj\" --no-build -c Release /p:PackageOutputPath=$packageOutputFolder /p:CI=true\n}\nWrite-Host \"Build Complete.\" -ForegroundColor \"Green\"\n"
  },
  {
    "path": "docs/_config.yml",
    "content": "theme: jekyll-theme-cayman"
  },
  {
    "path": "docs/index.md",
    "content": "# Dapper.Contrib - Extensions for Dapper\n\n## Overview\n\nA brief guide is available [on github](https://github.com/DapperLib/Dapper.Contrib/blob/main/Readme.md)\n\n## Installation\n\nFrom NuGet:\n\n    Install-Package Dapper.Contrib\n\nNote: to get the latest pre-release build, add ` -Pre` to the end of the command.\n\n## Release  Notes\n\n### Unreleased\n\n(note: new PRs will not be merged until they add release note wording here)\n\n### Previous Releases\n\nPrior to v2.0.90, Dapper.Contrib was part of the main repository - please see release notes at [https://dapperlib.github.io/Dapper/](https://dapperlib.github.io/Dapper/)\n"
  },
  {
    "path": "nuget.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n    <clear />\n    <add key=\"NuGet\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n</configuration>"
  },
  {
    "path": "src/Dapper.Contrib/Dapper.Contrib.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <AssemblyName>Dapper.Contrib</AssemblyName>\r\n    <Title>Dapper.Contrib</Title>\r\n    <PackageTags>orm;sql;micro-orm;dapper</PackageTags>\r\n    <Description>The official collection of get, insert, update and delete helpers for Dapper.net. Also handles lists of entities and optional \"dirty\" tracking of interface-based entities.</Description>\r\n    <Authors>Sam Saffron;Johan Danforth</Authors>\r\n    <TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks>\r\n    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>\r\n    <NoWarn>$(NoWarn);CA1050</NoWarn>\r\n  </PropertyGroup>\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Dapper\" Version=\"2.0.78\" />\r\n  </ItemGroup>\r\n  <ItemGroup Condition=\" '$(TargetFramework)' == 'netstandard2.0' \">\r\n    <PackageReference Include=\"System.Reflection.Emit\" Version=\"4.7.0\" />\r\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.7.0\" />\r\n  </ItemGroup>\r\n  <ItemGroup Condition=\" '$(TargetFramework)' == 'net461' \">\r\n    <Reference Include=\"Microsoft.CSharp\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/Dapper.Contrib/SqlMapperExtensions.Async.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Data;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing Dapper;\r\n\r\nnamespace Dapper.Contrib.Extensions\r\n{\r\n    public static partial class SqlMapperExtensions\r\n    {\r\n        /// <summary>\r\n        /// Returns a single entity by a single id from table \"Ts\" asynchronously using Task. T must be of interface type. \r\n        /// Id must be marked with [Key] attribute.\r\n        /// Created entity is tracked/intercepted for changes and used by the Update() extension. \r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Interface type to create and populate</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"id\">Id of the entity to get, must be marked with [Key] attribute</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>Entity of T</returns>\r\n        public static async Task<T> GetAsync<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            if (!GetQueries.TryGetValue(type.TypeHandle, out string sql))\r\n            {\r\n                var key = GetSingleKey<T>(nameof(GetAsync));\r\n                var name = GetTableName(type);\r\n\r\n                sql = $\"SELECT * FROM {name} WHERE {key.Name} = @id\";\r\n                GetQueries[type.TypeHandle] = sql;\r\n            }\r\n\r\n            var dynParams = new DynamicParameters();\r\n            dynParams.Add(\"@id\", id);\r\n\r\n            if (!type.IsInterface)\r\n                return (await connection.QueryAsync<T>(sql, dynParams, transaction, commandTimeout).ConfigureAwait(false)).FirstOrDefault();\r\n\r\n            if (!((await connection.QueryAsync<dynamic>(sql, dynParams).ConfigureAwait(false)).FirstOrDefault() is IDictionary<string, object> res))\r\n            {\r\n                return null;\r\n            }\r\n\r\n            var obj = ProxyGenerator.GetInterfaceProxy<T>();\r\n\r\n            foreach (var property in TypePropertiesCache(type))\r\n            {\r\n                var val = res[property.Name];\r\n                if (val == null) continue;\r\n                if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))\r\n                {\r\n                    var genericType = Nullable.GetUnderlyingType(property.PropertyType);\r\n                    if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null);\r\n                }\r\n                else\r\n                {\r\n                    property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);\r\n                }\r\n            }\r\n\r\n            ((IProxy)obj).IsDirty = false;   //reset change tracking and return\r\n\r\n            return obj;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Returns a list of entities from table \"Ts\".  \r\n        /// Id of T must be marked with [Key] attribute.\r\n        /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension\r\n        /// for optimal performance. \r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Interface or type to create and populate</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>Entity of T</returns>\r\n        public static Task<IEnumerable<T>> GetAllAsync<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            var cacheType = typeof(List<T>);\r\n\r\n            if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql))\r\n            {\r\n                GetSingleKey<T>(nameof(GetAll));\r\n                var name = GetTableName(type);\r\n\r\n                sql = \"SELECT * FROM \" + name;\r\n                GetQueries[cacheType.TypeHandle] = sql;\r\n            }\r\n\r\n            if (!type.IsInterface)\r\n            {\r\n                return connection.QueryAsync<T>(sql, null, transaction, commandTimeout);\r\n            }\r\n            return GetAllAsyncImpl<T>(connection, transaction, commandTimeout, sql, type);\r\n        }\r\n\r\n        private static async Task<IEnumerable<T>> GetAllAsyncImpl<T>(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string sql, Type type) where T : class\r\n        {\r\n            var result = await connection.QueryAsync(sql, transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false);\r\n            var list = new List<T>();\r\n            foreach (IDictionary<string, object> res in result)\r\n            {\r\n                var obj = ProxyGenerator.GetInterfaceProxy<T>();\r\n                foreach (var property in TypePropertiesCache(type))\r\n                {\r\n                    var val = res[property.Name];\r\n                    if (val == null) continue;\r\n                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))\r\n                    {\r\n                        var genericType = Nullable.GetUnderlyingType(property.PropertyType);\r\n                        if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null);\r\n                    }\r\n                    else\r\n                    {\r\n                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);\r\n                    }\r\n                }\r\n                ((IProxy)obj).IsDirty = false;   //reset change tracking and return\r\n                list.Add(obj);\r\n            }\r\n            return list;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Inserts an entity into table \"Ts\" asynchronously using Task and returns identity id.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">The type being inserted.</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToInsert\">Entity to insert</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <param name=\"sqlAdapter\">The specific ISqlAdapter to use, auto-detected based on connection if null</param>\r\n        /// <returns>Identity of inserted entity</returns>\r\n        public static Task<int> InsertAsync<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null,\r\n            int? commandTimeout = null, ISqlAdapter sqlAdapter = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            sqlAdapter ??= GetFormatter(connection);\r\n\r\n            var isList = false;\r\n            if (type.IsArray)\r\n            {\r\n                isList = true;\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    isList = true;\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var name = GetTableName(type);\r\n            var sbColumnList = new StringBuilder(null);\r\n            var allProperties = TypePropertiesCache(type);\r\n            var keyProperties = KeyPropertiesCache(type).ToList();\r\n            var computedProperties = ComputedPropertiesCache(type);\r\n            var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();\r\n\r\n            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)\r\n            {\r\n                var property = allPropertiesExceptKeyAndComputed[i];\r\n                sqlAdapter.AppendColumnName(sbColumnList, property.Name);\r\n                if (i < allPropertiesExceptKeyAndComputed.Count - 1)\r\n                    sbColumnList.Append(\", \");\r\n            }\r\n\r\n            var sbParameterList = new StringBuilder(null);\r\n            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)\r\n            {\r\n                var property = allPropertiesExceptKeyAndComputed[i];\r\n                sbParameterList.AppendFormat(\"@{0}\", property.Name);\r\n                if (i < allPropertiesExceptKeyAndComputed.Count - 1)\r\n                    sbParameterList.Append(\", \");\r\n            }\r\n\r\n            if (!isList)    //single entity\r\n            {\r\n                return sqlAdapter.InsertAsync(connection, transaction, commandTimeout, name, sbColumnList.ToString(),\r\n                    sbParameterList.ToString(), keyProperties, entityToInsert);\r\n            }\r\n\r\n            //insert list of entities\r\n            var cmd = $\"INSERT INTO {name} ({sbColumnList}) values ({sbParameterList})\";\r\n            return connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Updates entity in table \"Ts\" asynchronously using Task, checks if the entity is modified if the entity is tracked by the Get() extension.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type to be updated</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToUpdate\">Entity to be updated</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if updated, false if not found or not modified (tracked entities)</returns>\r\n        public static async Task<bool> UpdateAsync<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            if ((entityToUpdate is IProxy proxy) && !proxy.IsDirty)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            var type = typeof(T);\r\n\r\n            if (type.IsArray)\r\n            {\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var keyProperties = KeyPropertiesCache(type).ToList();\r\n            var explicitKeyProperties = ExplicitKeyPropertiesCache(type);\r\n            if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)\r\n                throw new ArgumentException(\"Entity must have at least one [Key] or [ExplicitKey] property\");\r\n\r\n            var name = GetTableName(type);\r\n\r\n            var sb = new StringBuilder();\r\n            sb.AppendFormat(\"update {0} set \", name);\r\n\r\n            var allProperties = TypePropertiesCache(type);\r\n            keyProperties.AddRange(explicitKeyProperties);\r\n            var computedProperties = ComputedPropertiesCache(type);\r\n            var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList();\r\n\r\n            var adapter = GetFormatter(connection);\r\n\r\n            for (var i = 0; i < nonIdProps.Count; i++)\r\n            {\r\n                var property = nonIdProps[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);\r\n                if (i < nonIdProps.Count - 1)\r\n                    sb.Append(\", \");\r\n            }\r\n            sb.Append(\" where \");\r\n            for (var i = 0; i < keyProperties.Count; i++)\r\n            {\r\n                var property = keyProperties[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);\r\n                if (i < keyProperties.Count - 1)\r\n                    sb.Append(\" and \");\r\n            }\r\n            var updated = await connection.ExecuteAsync(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction).ConfigureAwait(false);\r\n            return updated > 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Delete entity in table \"Ts\" asynchronously using Task.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type of entity</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToDelete\">Entity to delete</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if deleted, false if not found</returns>\r\n        public static async Task<bool> DeleteAsync<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            if (entityToDelete == null)\r\n                throw new ArgumentException(\"Cannot Delete null Object\", nameof(entityToDelete));\r\n\r\n            var type = typeof(T);\r\n\r\n            if (type.IsArray)\r\n            {\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var keyProperties = KeyPropertiesCache(type);\r\n            var explicitKeyProperties = ExplicitKeyPropertiesCache(type);\r\n            if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)\r\n                throw new ArgumentException(\"Entity must have at least one [Key] or [ExplicitKey] property\");\r\n\r\n            var name = GetTableName(type);\r\n            var allKeyProperties = keyProperties.Concat(explicitKeyProperties).ToList();\r\n\r\n            var sb = new StringBuilder();\r\n            sb.AppendFormat(\"DELETE FROM {0} WHERE \", name);\r\n\r\n            var adapter = GetFormatter(connection);\r\n\r\n            for (var i = 0; i < allKeyProperties.Count; i++)\r\n            {\r\n                var property = allKeyProperties[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);\r\n                if (i < allKeyProperties.Count - 1)\r\n                    sb.Append(\" AND \");\r\n            }\r\n            var deleted = await connection.ExecuteAsync(sb.ToString(), entityToDelete, transaction, commandTimeout).ConfigureAwait(false);\r\n            return deleted > 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Delete all entities in the table related to the type T asynchronously using Task.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type of entity</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if deleted, false if none found</returns>\r\n        public static async Task<bool> DeleteAllAsync<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            var statement = \"DELETE FROM \" + GetTableName(type);\r\n            var deleted = await connection.ExecuteAsync(statement, null, transaction, commandTimeout).ConfigureAwait(false);\r\n            return deleted > 0;\r\n        }\r\n    }\r\n}\r\n\r\npublic partial interface ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);\r\n}\r\n\r\npublic partial class SqlServerAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"INSERT INTO {tableName} ({columnList}) values ({parameterList}); SELECT SCOPE_IDENTITY() id\";\r\n        var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n\r\n        var first = await multi.ReadFirstOrDefaultAsync().ConfigureAwait(false);\r\n        if (first == null || first.id == null) return 0;\r\n\r\n        var id = (int)first.id;\r\n        var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (pi.Length == 0) return id;\r\n\r\n        var idp = pi[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n}\r\n\r\npublic partial class SqlCeServerAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList})\";\r\n        await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n        var r = (await connection.QueryAsync<dynamic>(\"SELECT @@IDENTITY id\", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false)).ToList();\r\n\r\n        if (r[0] == null || r[0].id == null) return 0;\r\n        var id = (int)r[0].id;\r\n\r\n        var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (pi.Length == 0) return id;\r\n\r\n        var idp = pi[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n}\r\n\r\npublic partial class MySqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName,\r\n        string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList})\";\r\n        await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n        var r = await connection.QueryAsync<dynamic>(\"SELECT LAST_INSERT_ID() id\", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false);\r\n\r\n        var id = r.First().id;\r\n        if (id == null) return 0;\r\n        var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (pi.Length == 0) return Convert.ToInt32(id);\r\n\r\n        var idp = pi[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return Convert.ToInt32(id);\r\n    }\r\n}\r\n\r\npublic partial class PostgresAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var sb = new StringBuilder();\r\n        sb.AppendFormat(\"INSERT INTO {0} ({1}) VALUES ({2})\", tableName, columnList, parameterList);\r\n\r\n        // If no primary key then safe to assume a join table with not too much data to return\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0)\r\n        {\r\n            sb.Append(\" RETURNING *\");\r\n        }\r\n        else\r\n        {\r\n            sb.Append(\" RETURNING \");\r\n            bool first = true;\r\n            foreach (var property in propertyInfos)\r\n            {\r\n                if (!first)\r\n                    sb.Append(\", \");\r\n                first = false;\r\n                sb.Append(property.Name);\r\n            }\r\n        }\r\n\r\n        var results = await connection.QueryAsync(sb.ToString(), entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n\r\n        // Return the key by assigning the corresponding property in the object - by product is that it supports compound primary keys\r\n        var id = 0;\r\n        foreach (var p in propertyInfos)\r\n        {\r\n            var value = ((IDictionary<string, object>)results.First())[p.Name.ToLower()];\r\n            p.SetValue(entityToInsert, value, null);\r\n            if (id == 0)\r\n                id = Convert.ToInt32(value);\r\n        }\r\n        return id;\r\n    }\r\n}\r\n\r\npublic partial class SQLiteAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}); SELECT last_insert_rowid() id\";\r\n        var multi = await connection.QueryMultipleAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n\r\n        var id = (int)(await multi.ReadFirstAsync().ConfigureAwait(false)).id;\r\n        var pi = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (pi.Length == 0) return id;\r\n\r\n        var idp = pi[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n}\r\n\r\npublic partial class FbAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"insert into {tableName} ({columnList}) values ({parameterList})\";\r\n        await connection.ExecuteAsync(cmd, entityToInsert, transaction, commandTimeout).ConfigureAwait(false);\r\n\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        var keyName = propertyInfos[0].Name;\r\n        var r = await connection.QueryAsync($\"SELECT FIRST 1 {keyName} ID FROM {tableName} ORDER BY {keyName} DESC\", transaction: transaction, commandTimeout: commandTimeout).ConfigureAwait(false);\r\n\r\n        var id = r.First().ID;\r\n        if (id == null) return 0;\r\n        if (propertyInfos.Length == 0) return Convert.ToInt32(id);\r\n\r\n        var idp = propertyInfos[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return Convert.ToInt32(id);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Dapper.Contrib/SqlMapperExtensions.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Data;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text;\r\nusing System.Collections.Concurrent;\r\nusing System.Reflection.Emit;\r\nusing System.Threading;\r\n\r\nusing Dapper;\r\n\r\nnamespace Dapper.Contrib.Extensions\r\n{\r\n    /// <summary>\r\n    /// The Dapper.Contrib extensions for Dapper\r\n    /// </summary>\r\n    public static partial class SqlMapperExtensions\r\n    {\r\n        /// <summary>\r\n        /// Defined a proxy object with a possibly dirty state.\r\n        /// </summary>\r\n        public interface IProxy //must be kept public\r\n        {\r\n            /// <summary>\r\n            /// Whether the object has been changed.\r\n            /// </summary>\r\n            bool IsDirty { get; set; }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Defines a table name mapper for getting table names from types.\r\n        /// </summary>\r\n        public interface ITableNameMapper\r\n        {\r\n            /// <summary>\r\n            /// Gets a table name from a given <see cref=\"Type\"/>.\r\n            /// </summary>\r\n            /// <param name=\"type\">The <see cref=\"Type\"/> to get a name from.</param>\r\n            /// <returns>The table name for the given <paramref name=\"type\"/>.</returns>\r\n            string GetTableName(Type type);\r\n        }\r\n\r\n        /// <summary>\r\n        /// The function to get a database type from the given <see cref=\"IDbConnection\"/>.\r\n        /// </summary>\r\n        /// <param name=\"connection\">The connection to get a database type name from.</param>\r\n        public delegate string GetDatabaseTypeDelegate(IDbConnection connection);\r\n        /// <summary>\r\n        /// The function to get a table name from a given <see cref=\"Type\"/>\r\n        /// </summary>\r\n        /// <param name=\"type\">The <see cref=\"Type\"/> to get a table name for.</param>\r\n        public delegate string TableNameMapperDelegate(Type type);\r\n\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ExplicitKeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> GetQueries = new ConcurrentDictionary<RuntimeTypeHandle, string>();\r\n        private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeTableName = new ConcurrentDictionary<RuntimeTypeHandle, string>();\r\n\r\n        private static readonly ISqlAdapter DefaultAdapter = new SqlServerAdapter();\r\n        private static readonly Dictionary<string, ISqlAdapter> AdapterDictionary\r\n            = new Dictionary<string, ISqlAdapter>(6)\r\n            {\r\n                [\"sqlconnection\"] = new SqlServerAdapter(),\r\n                [\"sqlceconnection\"] = new SqlCeServerAdapter(),\r\n                [\"npgsqlconnection\"] = new PostgresAdapter(),\r\n                [\"sqliteconnection\"] = new SQLiteAdapter(),\r\n                [\"mysqlconnection\"] = new MySqlAdapter(),\r\n                [\"fbconnection\"] = new FbAdapter()\r\n            };\r\n\r\n        private static List<PropertyInfo> ComputedPropertiesCache(Type type)\r\n        {\r\n            if (ComputedProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))\r\n            {\r\n                return pi.ToList();\r\n            }\r\n\r\n            var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ComputedAttribute)).ToList();\r\n\r\n            ComputedProperties[type.TypeHandle] = computedProperties;\r\n            return computedProperties;\r\n        }\r\n\r\n        private static List<PropertyInfo> ExplicitKeyPropertiesCache(Type type)\r\n        {\r\n            if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))\r\n            {\r\n                return pi.ToList();\r\n            }\r\n\r\n            var explicitKeyProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)).ToList();\r\n\r\n            ExplicitKeyProperties[type.TypeHandle] = explicitKeyProperties;\r\n            return explicitKeyProperties;\r\n        }\r\n\r\n        private static List<PropertyInfo> KeyPropertiesCache(Type type)\r\n        {\r\n            if (KeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))\r\n            {\r\n                return pi.ToList();\r\n            }\r\n\r\n            var allProperties = TypePropertiesCache(type);\r\n            var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();\r\n\r\n            if (keyProperties.Count == 0)\r\n            {\r\n                var idProp = allProperties.Find(p => string.Equals(p.Name, \"id\", StringComparison.CurrentCultureIgnoreCase));\r\n                if (idProp != null && !idProp.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute))\r\n                {\r\n                    keyProperties.Add(idProp);\r\n                }\r\n            }\r\n\r\n            KeyProperties[type.TypeHandle] = keyProperties;\r\n            return keyProperties;\r\n        }\r\n\r\n        private static List<PropertyInfo> TypePropertiesCache(Type type)\r\n        {\r\n            if (TypeProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pis))\r\n            {\r\n                return pis.ToList();\r\n            }\r\n\r\n            var properties = type.GetProperties().Where(IsWriteable).ToArray();\r\n            TypeProperties[type.TypeHandle] = properties;\r\n            return properties.ToList();\r\n        }\r\n\r\n        private static bool IsWriteable(PropertyInfo pi)\r\n        {\r\n            var attributes = pi.GetCustomAttributes(typeof(WriteAttribute), false).AsList();\r\n            if (attributes.Count != 1) return true;\r\n\r\n            var writeAttribute = (WriteAttribute)attributes[0];\r\n            return writeAttribute.Write;\r\n        }\r\n\r\n        private static PropertyInfo GetSingleKey<T>(string method)\r\n        {\r\n            var type = typeof(T);\r\n            var keys = KeyPropertiesCache(type);\r\n            var explicitKeys = ExplicitKeyPropertiesCache(type);\r\n            var keyCount = keys.Count + explicitKeys.Count;\r\n            if (keyCount > 1)\r\n                throw new DataException($\"{method}<T> only supports an entity with a single [Key] or [ExplicitKey] property. [Key] Count: {keys.Count}, [ExplicitKey] Count: {explicitKeys.Count}\");\r\n            if (keyCount == 0)\r\n                throw new DataException($\"{method}<T> only supports an entity with a [Key] or an [ExplicitKey] property\");\r\n\r\n            return keys.Count > 0 ? keys[0] : explicitKeys[0];\r\n        }\r\n\r\n        /// <summary>\r\n        /// Returns a single entity by a single id from table \"Ts\".  \r\n        /// Id must be marked with [Key] attribute.\r\n        /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension\r\n        /// for optimal performance. \r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Interface or type to create and populate</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"id\">Id of the entity to get, must be marked with [Key] attribute</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>Entity of T</returns>\r\n        public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n\r\n            if (!GetQueries.TryGetValue(type.TypeHandle, out string sql))\r\n            {\r\n                var key = GetSingleKey<T>(nameof(Get));\r\n                var name = GetTableName(type);\r\n\r\n                sql = $\"select * from {name} where {key.Name} = @id\";\r\n                GetQueries[type.TypeHandle] = sql;\r\n            }\r\n\r\n            var dynParams = new DynamicParameters();\r\n            dynParams.Add(\"@id\", id);\r\n\r\n            T obj;\r\n\r\n            if (type.IsInterface)\r\n            {\r\n                if (!(connection.Query(sql, dynParams).FirstOrDefault() is IDictionary<string, object> res))\r\n                {\r\n                    return null;\r\n                }\r\n\r\n                obj = ProxyGenerator.GetInterfaceProxy<T>();\r\n\r\n                foreach (var property in TypePropertiesCache(type))\r\n                {\r\n                    var val = res[property.Name];\r\n                    if (val == null) continue;\r\n                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))\r\n                    {\r\n                        var genericType = Nullable.GetUnderlyingType(property.PropertyType);\r\n                        if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null);\r\n                    }\r\n                    else\r\n                    {\r\n                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);\r\n                    }\r\n                }\r\n\r\n                ((IProxy)obj).IsDirty = false;   //reset change tracking and return\r\n            }\r\n            else\r\n            {\r\n                obj = connection.Query<T>(sql, dynParams, transaction, commandTimeout: commandTimeout).FirstOrDefault();\r\n            }\r\n            return obj;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Returns a list of entities from table \"Ts\".\r\n        /// Id of T must be marked with [Key] attribute.\r\n        /// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension\r\n        /// for optimal performance.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Interface or type to create and populate</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>Entity of T</returns>\r\n        public static IEnumerable<T> GetAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            var cacheType = typeof(List<T>);\r\n\r\n            if (!GetQueries.TryGetValue(cacheType.TypeHandle, out string sql))\r\n            {\r\n                GetSingleKey<T>(nameof(GetAll));\r\n                var name = GetTableName(type);\r\n\r\n                sql = \"select * from \" + name;\r\n                GetQueries[cacheType.TypeHandle] = sql;\r\n            }\r\n\r\n            if (!type.IsInterface) return connection.Query<T>(sql, null, transaction, commandTimeout: commandTimeout);\r\n\r\n            var result = connection.Query(sql);\r\n            var list = new List<T>();\r\n            foreach (IDictionary<string, object> res in result)\r\n            {\r\n                var obj = ProxyGenerator.GetInterfaceProxy<T>();\r\n                foreach (var property in TypePropertiesCache(type))\r\n                {\r\n                    var val = res[property.Name];\r\n                    if (val == null) continue;\r\n                    if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))\r\n                    {\r\n                        var genericType = Nullable.GetUnderlyingType(property.PropertyType);\r\n                        if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null);\r\n                    }\r\n                    else\r\n                    {\r\n                        property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null);\r\n                    }\r\n                }\r\n                ((IProxy)obj).IsDirty = false;   //reset change tracking and return\r\n                list.Add(obj);\r\n            }\r\n            return list;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Specify a custom table name mapper based on the POCO type name\r\n        /// </summary>\r\n#pragma warning disable CA2211 // Non-constant fields should not be visible - I agree with you, but we can't do that until we break the API\r\n        public static TableNameMapperDelegate TableNameMapper;\r\n#pragma warning restore CA2211 // Non-constant fields should not be visible\r\n\r\n        private static string GetTableName(Type type)\r\n        {\r\n            if (TypeTableName.TryGetValue(type.TypeHandle, out string name)) return name;\r\n\r\n            if (TableNameMapper != null)\r\n            {\r\n                name = TableNameMapper(type);\r\n            }\r\n            else\r\n            {\r\n                //NOTE: This as dynamic trick falls back to handle both our own Table-attribute as well as the one in EntityFramework \r\n                var tableAttrName =\r\n                    type.GetCustomAttribute<TableAttribute>(false)?.Name\r\n                    ?? (type.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().Name == \"TableAttribute\") as dynamic)?.Name;\r\n\r\n                if (tableAttrName != null)\r\n                {\r\n                    name = tableAttrName;\r\n                }\r\n                else\r\n                {\r\n                    name = type.Name + \"s\";\r\n                    if (type.IsInterface && name.StartsWith(\"I\"))\r\n                        name = name.Substring(1);\r\n                }\r\n            }\r\n\r\n            TypeTableName[type.TypeHandle] = name;\r\n            return name;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Inserts an entity into table \"Ts\" and returns identity id or number of inserted rows if inserting a list.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">The type to insert.</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToInsert\">Entity to insert, can be list of entities</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>Identity of inserted entity, or number of inserted rows if inserting a list</returns>\r\n        public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var isList = false;\r\n\r\n            var type = typeof(T);\r\n\r\n            if (type.IsArray)\r\n            {\r\n                isList = true;\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    isList = true;\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var name = GetTableName(type);\r\n            var sbColumnList = new StringBuilder(null);\r\n            var allProperties = TypePropertiesCache(type);\r\n            var keyProperties = KeyPropertiesCache(type);\r\n            var computedProperties = ComputedPropertiesCache(type);\r\n            var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();\r\n\r\n            var adapter = GetFormatter(connection);\r\n\r\n            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)\r\n            {\r\n                var property = allPropertiesExceptKeyAndComputed[i];\r\n                adapter.AppendColumnName(sbColumnList, property.Name);  //fix for issue #336\r\n                if (i < allPropertiesExceptKeyAndComputed.Count - 1)\r\n                    sbColumnList.Append(\", \");\r\n            }\r\n\r\n            var sbParameterList = new StringBuilder(null);\r\n            for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)\r\n            {\r\n                var property = allPropertiesExceptKeyAndComputed[i];\r\n                sbParameterList.AppendFormat(\"@{0}\", property.Name);\r\n                if (i < allPropertiesExceptKeyAndComputed.Count - 1)\r\n                    sbParameterList.Append(\", \");\r\n            }\r\n\r\n            int returnVal;\r\n            var wasClosed = connection.State == ConnectionState.Closed;\r\n            if (wasClosed) connection.Open();\r\n\r\n            if (!isList)    //single entity\r\n            {\r\n                returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),\r\n                    sbParameterList.ToString(), keyProperties, entityToInsert);\r\n            }\r\n            else\r\n            {\r\n                //insert list of entities\r\n                var cmd = $\"insert into {name} ({sbColumnList}) values ({sbParameterList})\";\r\n                returnVal = connection.Execute(cmd, entityToInsert, transaction, commandTimeout);\r\n            }\r\n            if (wasClosed) connection.Close();\r\n            return returnVal;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Updates entity in table \"Ts\", checks if the entity is modified if the entity is tracked by the Get() extension.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type to be updated</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToUpdate\">Entity to be updated</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if updated, false if not found or not modified (tracked entities)</returns>\r\n        public static bool Update<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            if (entityToUpdate is IProxy proxy && !proxy.IsDirty)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            var type = typeof(T);\r\n\r\n            if (type.IsArray)\r\n            {\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var keyProperties = KeyPropertiesCache(type).ToList();  //added ToList() due to issue #418, must work on a list copy\r\n            var explicitKeyProperties = ExplicitKeyPropertiesCache(type);\r\n            if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)\r\n                throw new ArgumentException(\"Entity must have at least one [Key] or [ExplicitKey] property\");\r\n\r\n            var name = GetTableName(type);\r\n\r\n            var sb = new StringBuilder();\r\n            sb.AppendFormat(\"update {0} set \", name);\r\n\r\n            var allProperties = TypePropertiesCache(type);\r\n            keyProperties.AddRange(explicitKeyProperties);\r\n            var computedProperties = ComputedPropertiesCache(type);\r\n            var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList();\r\n\r\n            var adapter = GetFormatter(connection);\r\n\r\n            for (var i = 0; i < nonIdProps.Count; i++)\r\n            {\r\n                var property = nonIdProps[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336\r\n                if (i < nonIdProps.Count - 1)\r\n                    sb.Append(\", \");\r\n            }\r\n            sb.Append(\" where \");\r\n            for (var i = 0; i < keyProperties.Count; i++)\r\n            {\r\n                var property = keyProperties[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336\r\n                if (i < keyProperties.Count - 1)\r\n                    sb.Append(\" and \");\r\n            }\r\n            var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);\r\n            return updated > 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Delete entity in table \"Ts\".\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type of entity</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"entityToDelete\">Entity to delete</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if deleted, false if not found</returns>\r\n        public static bool Delete<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            if (entityToDelete == null)\r\n                throw new ArgumentException(\"Cannot Delete null Object\", nameof(entityToDelete));\r\n\r\n            var type = typeof(T);\r\n\r\n            if (type.IsArray)\r\n            {\r\n                type = type.GetElementType();\r\n            }\r\n            else if (type.IsGenericType)\r\n            {\r\n                var typeInfo = type.GetTypeInfo();\r\n                bool implementsGenericIEnumerableOrIsGenericIEnumerable =\r\n                    typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||\r\n                    typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);\r\n\r\n                if (implementsGenericIEnumerableOrIsGenericIEnumerable)\r\n                {\r\n                    type = type.GetGenericArguments()[0];\r\n                }\r\n            }\r\n\r\n            var keyProperties = KeyPropertiesCache(type).ToList();  //added ToList() due to issue #418, must work on a list copy\r\n            var explicitKeyProperties = ExplicitKeyPropertiesCache(type);\r\n            if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)\r\n                throw new ArgumentException(\"Entity must have at least one [Key] or [ExplicitKey] property\");\r\n\r\n            var name = GetTableName(type);\r\n            keyProperties.AddRange(explicitKeyProperties);\r\n\r\n            var sb = new StringBuilder();\r\n            sb.AppendFormat(\"delete from {0} where \", name);\r\n\r\n            var adapter = GetFormatter(connection);\r\n\r\n            for (var i = 0; i < keyProperties.Count; i++)\r\n            {\r\n                var property = keyProperties[i];\r\n                adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336\r\n                if (i < keyProperties.Count - 1)\r\n                    sb.Append(\" and \");\r\n            }\r\n            var deleted = connection.Execute(sb.ToString(), entityToDelete, transaction, commandTimeout);\r\n            return deleted > 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Delete all entities in the table related to the type T.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">Type of entity</typeparam>\r\n        /// <param name=\"connection\">Open SqlConnection</param>\r\n        /// <param name=\"transaction\">The transaction to run under, null (the default) if none</param>\r\n        /// <param name=\"commandTimeout\">Number of seconds before command execution timeout</param>\r\n        /// <returns>true if deleted, false if none found</returns>\r\n        public static bool DeleteAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class\r\n        {\r\n            var type = typeof(T);\r\n            var name = GetTableName(type);\r\n            var statement = $\"delete from {name}\";\r\n            var deleted = connection.Execute(statement, null, transaction, commandTimeout);\r\n            return deleted > 0;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Specifies a custom callback that detects the database type instead of relying on the default strategy (the name of the connection type object).\r\n        /// Please note that this callback is global and will be used by all the calls that require a database specific adapter.\r\n        /// </summary>\r\n#pragma warning disable CA2211 // Non-constant fields should not be visible - I agree with you, but we can't do that until we break the API\r\n        public static GetDatabaseTypeDelegate GetDatabaseType;\r\n#pragma warning restore CA2211 // Non-constant fields should not be visible\r\n\r\n        private static ISqlAdapter GetFormatter(IDbConnection connection)\r\n        {\r\n            var name = GetDatabaseType?.Invoke(connection).ToLower()\r\n                       ?? connection.GetType().Name.ToLower();\r\n\r\n            return AdapterDictionary.TryGetValue(name, out var adapter)\r\n                ? adapter\r\n                : DefaultAdapter;\r\n        }\r\n\r\n        private static class ProxyGenerator\r\n        {\r\n            private static readonly Dictionary<Type, Type> TypeCache = new Dictionary<Type, Type>();\r\n\r\n            private static AssemblyBuilder GetAsmBuilder(string name)\r\n            {\r\n#if !NET461\r\n                return AssemblyBuilder.DefineDynamicAssembly(new AssemblyName { Name = name }, AssemblyBuilderAccess.Run);\r\n#else\r\n                return Thread.GetDomain().DefineDynamicAssembly(new AssemblyName { Name = name }, AssemblyBuilderAccess.Run);\r\n#endif\r\n            }\r\n\r\n            public static T GetInterfaceProxy<T>()\r\n            {\r\n                Type typeOfT = typeof(T);\r\n\r\n                if (TypeCache.TryGetValue(typeOfT, out Type k))\r\n                {\r\n                    return (T)Activator.CreateInstance(k);\r\n                }\r\n                var assemblyBuilder = GetAsmBuilder(typeOfT.Name);\r\n\r\n                var moduleBuilder = assemblyBuilder.DefineDynamicModule(\"SqlMapperExtensions.\" + typeOfT.Name); //NOTE: to save, add \"asdasd.dll\" parameter\r\n\r\n                var interfaceType = typeof(IProxy);\r\n                var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + \"_\" + Guid.NewGuid(),\r\n                    TypeAttributes.Public | TypeAttributes.Class);\r\n                typeBuilder.AddInterfaceImplementation(typeOfT);\r\n                typeBuilder.AddInterfaceImplementation(interfaceType);\r\n\r\n                //create our _isDirty field, which implements IProxy\r\n                var setIsDirtyMethod = CreateIsDirtyProperty(typeBuilder);\r\n\r\n                // Generate a field for each property, which implements the T\r\n                foreach (var property in typeof(T).GetProperties())\r\n                {\r\n                    var isId = property.GetCustomAttributes(true).Any(a => a is KeyAttribute);\r\n                    CreateProperty<T>(typeBuilder, property.Name, property.PropertyType, setIsDirtyMethod, isId);\r\n                }\r\n\r\n#if NETSTANDARD2_0\r\n                var generatedType = typeBuilder.CreateTypeInfo().AsType();\r\n#else\r\n                var generatedType = typeBuilder.CreateType();\r\n#endif\r\n\r\n                TypeCache.Add(typeOfT, generatedType);\r\n                return (T)Activator.CreateInstance(generatedType);\r\n            }\r\n\r\n            private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder)\r\n            {\r\n                var propType = typeof(bool);\r\n                var field = typeBuilder.DefineField(\"_\" + nameof(IProxy.IsDirty), propType, FieldAttributes.Private);\r\n                var property = typeBuilder.DefineProperty(nameof(IProxy.IsDirty),\r\n                                               System.Reflection.PropertyAttributes.None,\r\n                                               propType,\r\n                                               new[] { propType });\r\n\r\n                const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.SpecialName\r\n                                                  | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig;\r\n\r\n                // Define the \"get\" and \"set\" accessor methods\r\n                var currGetPropMthdBldr = typeBuilder.DefineMethod(\"get_\" + nameof(IProxy.IsDirty),\r\n                                             getSetAttr,\r\n                                             propType,\r\n                                             Type.EmptyTypes);\r\n                var currGetIl = currGetPropMthdBldr.GetILGenerator();\r\n                currGetIl.Emit(OpCodes.Ldarg_0);\r\n                currGetIl.Emit(OpCodes.Ldfld, field);\r\n                currGetIl.Emit(OpCodes.Ret);\r\n                var currSetPropMthdBldr = typeBuilder.DefineMethod(\"set_\" + nameof(IProxy.IsDirty),\r\n                                             getSetAttr,\r\n                                             null,\r\n                                             new[] { propType });\r\n                var currSetIl = currSetPropMthdBldr.GetILGenerator();\r\n                currSetIl.Emit(OpCodes.Ldarg_0);\r\n                currSetIl.Emit(OpCodes.Ldarg_1);\r\n                currSetIl.Emit(OpCodes.Stfld, field);\r\n                currSetIl.Emit(OpCodes.Ret);\r\n\r\n                property.SetGetMethod(currGetPropMthdBldr);\r\n                property.SetSetMethod(currSetPropMthdBldr);\r\n                var getMethod = typeof(IProxy).GetMethod(\"get_\" + nameof(IProxy.IsDirty));\r\n                var setMethod = typeof(IProxy).GetMethod(\"set_\" + nameof(IProxy.IsDirty));\r\n                typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);\r\n                typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);\r\n\r\n                return currSetPropMthdBldr;\r\n            }\r\n\r\n            private static void CreateProperty<T>(TypeBuilder typeBuilder, string propertyName, Type propType, MethodInfo setIsDirtyMethod, bool isIdentity)\r\n            {\r\n                //Define the field and the property \r\n                var field = typeBuilder.DefineField(\"_\" + propertyName, propType, FieldAttributes.Private);\r\n                var property = typeBuilder.DefineProperty(propertyName,\r\n                                               System.Reflection.PropertyAttributes.None,\r\n                                               propType,\r\n                                               new[] { propType });\r\n\r\n                const MethodAttributes getSetAttr = MethodAttributes.Public\r\n                                                    | MethodAttributes.Virtual\r\n                                                    | MethodAttributes.HideBySig;\r\n\r\n                // Define the \"get\" and \"set\" accessor methods\r\n                var currGetPropMthdBldr = typeBuilder.DefineMethod(\"get_\" + propertyName,\r\n                                             getSetAttr,\r\n                                             propType,\r\n                                             Type.EmptyTypes);\r\n\r\n                var currGetIl = currGetPropMthdBldr.GetILGenerator();\r\n                currGetIl.Emit(OpCodes.Ldarg_0);\r\n                currGetIl.Emit(OpCodes.Ldfld, field);\r\n                currGetIl.Emit(OpCodes.Ret);\r\n\r\n                var currSetPropMthdBldr = typeBuilder.DefineMethod(\"set_\" + propertyName,\r\n                                             getSetAttr,\r\n                                             null,\r\n                                             new[] { propType });\r\n\r\n                //store value in private field and set the isdirty flag\r\n                var currSetIl = currSetPropMthdBldr.GetILGenerator();\r\n                currSetIl.Emit(OpCodes.Ldarg_0);\r\n                currSetIl.Emit(OpCodes.Ldarg_1);\r\n                currSetIl.Emit(OpCodes.Stfld, field);\r\n                currSetIl.Emit(OpCodes.Ldarg_0);\r\n                currSetIl.Emit(OpCodes.Ldc_I4_1);\r\n                currSetIl.Emit(OpCodes.Call, setIsDirtyMethod);\r\n                currSetIl.Emit(OpCodes.Ret);\r\n\r\n                //TODO: Should copy all attributes defined by the interface?\r\n                if (isIdentity)\r\n                {\r\n                    var keyAttribute = typeof(KeyAttribute);\r\n                    var myConstructorInfo = keyAttribute.GetConstructor(Type.EmptyTypes);\r\n                    var attributeBuilder = new CustomAttributeBuilder(myConstructorInfo, Array.Empty<object>());\r\n                    property.SetCustomAttribute(attributeBuilder);\r\n                }\r\n\r\n                property.SetGetMethod(currGetPropMthdBldr);\r\n                property.SetSetMethod(currSetPropMthdBldr);\r\n                var getMethod = typeof(T).GetMethod(\"get_\" + propertyName);\r\n                var setMethod = typeof(T).GetMethod(\"set_\" + propertyName);\r\n                typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);\r\n                typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);\r\n            }\r\n        }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Defines the name of a table to use in Dapper.Contrib commands.\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Class)]\r\n    public class TableAttribute : Attribute\r\n    {\r\n        /// <summary>\r\n        /// Creates a table mapping to a specific name for Dapper.Contrib commands\r\n        /// </summary>\r\n        /// <param name=\"tableName\">The name of this table in the database.</param>\r\n        public TableAttribute(string tableName)\r\n        {\r\n            Name = tableName;\r\n        }\r\n\r\n        /// <summary>\r\n        /// The name of the table in the database\r\n        /// </summary>\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Specifies that this field is a primary key in the database\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Property)]\r\n    public class KeyAttribute : Attribute\r\n    {\r\n    }\r\n\r\n    /// <summary>\r\n    /// Specifies that this field is an explicitly set primary key in the database\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Property)]\r\n    public class ExplicitKeyAttribute : Attribute\r\n    {\r\n    }\r\n\r\n    /// <summary>\r\n    /// Specifies whether a field is writable in the database.\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Property)]\r\n    public class WriteAttribute : Attribute\r\n    {\r\n        /// <summary>\r\n        /// Specifies whether a field is writable in the database.\r\n        /// </summary>\r\n        /// <param name=\"write\">Whether a field is writable in the database.</param>\r\n        public WriteAttribute(bool write)\r\n        {\r\n            Write = write;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Whether a field is writable in the database.\r\n        /// </summary>\r\n        public bool Write { get; }\r\n    }\r\n\r\n    /// <summary>\r\n    /// Specifies that this is a computed column.\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Property)]\r\n    public class ComputedAttribute : Attribute\r\n    {\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The interface for all Dapper.Contrib database operations\r\n/// Implementing this is each provider's model.\r\n/// </summary>\r\npublic partial interface ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    void AppendColumnName(StringBuilder sb, string columnName);\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    void AppendColumnNameEqualsValue(StringBuilder sb, string columnName);\r\n}\r\n\r\n/// <summary>\r\n/// The SQL Server database adapter.\r\n/// </summary>\r\npublic partial class SqlServerAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"insert into {tableName} ({columnList}) values ({parameterList});select SCOPE_IDENTITY() id\";\r\n        var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);\r\n\r\n        var first = multi.Read().FirstOrDefault();\r\n        if (first == null || first.id == null) return 0;\r\n\r\n        var id = (int)first.id;\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0) return id;\r\n\r\n        var idProperty = propertyInfos[0];\r\n        idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"[{0}]\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"[{0}] = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The SQL Server Compact Edition database adapter.\r\n/// </summary>\r\npublic partial class SqlCeServerAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"insert into {tableName} ({columnList}) values ({parameterList})\";\r\n        connection.Execute(cmd, entityToInsert, transaction, commandTimeout);\r\n        var r = connection.Query(\"select @@IDENTITY id\", transaction: transaction, commandTimeout: commandTimeout).ToList();\r\n\r\n        if (r[0].id == null) return 0;\r\n        var id = (int)r[0].id;\r\n\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0) return id;\r\n\r\n        var idProperty = propertyInfos[0];\r\n        idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"[{0}]\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"[{0}] = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The MySQL database adapter.\r\n/// </summary>\r\npublic partial class MySqlAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"insert into {tableName} ({columnList}) values ({parameterList})\";\r\n        connection.Execute(cmd, entityToInsert, transaction, commandTimeout);\r\n        var r = connection.Query(\"Select LAST_INSERT_ID() id\", transaction: transaction, commandTimeout: commandTimeout);\r\n\r\n        var id = r.First().id;\r\n        if (id == null) return 0;\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0) return Convert.ToInt32(id);\r\n\r\n        var idp = propertyInfos[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return Convert.ToInt32(id);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"`{0}`\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"`{0}` = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The Postgres database adapter.\r\n/// </summary>\r\npublic partial class PostgresAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var sb = new StringBuilder();\r\n        sb.AppendFormat(\"insert into {0} ({1}) values ({2})\", tableName, columnList, parameterList);\r\n\r\n        // If no primary key then safe to assume a join table with not too much data to return\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0)\r\n        {\r\n            sb.Append(\" RETURNING *\");\r\n        }\r\n        else\r\n        {\r\n            sb.Append(\" RETURNING \");\r\n            var first = true;\r\n            foreach (var property in propertyInfos)\r\n            {\r\n                if (!first)\r\n                    sb.Append(\", \");\r\n                first = false;\r\n                sb.Append(property.Name);\r\n            }\r\n        }\r\n\r\n        var results = connection.Query(sb.ToString(), entityToInsert, transaction, commandTimeout: commandTimeout).ToList();\r\n\r\n        // Return the key by assigning the corresponding property in the object - by product is that it supports compound primary keys\r\n        var id = 0;\r\n        foreach (var p in propertyInfos)\r\n        {\r\n            var value = ((IDictionary<string, object>)results[0])[p.Name.ToLower()];\r\n            p.SetValue(entityToInsert, value, null);\r\n            if (id == 0)\r\n                id = Convert.ToInt32(value);\r\n        }\r\n        return id;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"\\\"{0}\\\"\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"\\\"{0}\\\" = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The SQLite database adapter.\r\n/// </summary>\r\npublic partial class SQLiteAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"INSERT INTO {tableName} ({columnList}) VALUES ({parameterList}); SELECT last_insert_rowid() id\";\r\n        var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);\r\n\r\n        var id = (int)multi.Read().First().id;\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        if (propertyInfos.Length == 0) return id;\r\n\r\n        var idProperty = propertyInfos[0];\r\n        idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);\r\n\r\n        return id;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"\\\"{0}\\\"\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"\\\"{0}\\\" = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n\r\n/// <summary>\r\n/// The Firebase SQL adapter.\r\n/// </summary>\r\npublic partial class FbAdapter : ISqlAdapter\r\n{\r\n    /// <summary>\r\n    /// Inserts <paramref name=\"entityToInsert\"/> into the database, returning the Id of the row created.\r\n    /// </summary>\r\n    /// <param name=\"connection\">The connection to use.</param>\r\n    /// <param name=\"transaction\">The transaction to use.</param>\r\n    /// <param name=\"commandTimeout\">The command timeout to use.</param>\r\n    /// <param name=\"tableName\">The table to insert into.</param>\r\n    /// <param name=\"columnList\">The columns to set with this insert.</param>\r\n    /// <param name=\"parameterList\">The parameters to set for this insert.</param>\r\n    /// <param name=\"keyProperties\">The key columns in this table.</param>\r\n    /// <param name=\"entityToInsert\">The entity to insert.</param>\r\n    /// <returns>The Id of the row created.</returns>\r\n    public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)\r\n    {\r\n        var cmd = $\"insert into {tableName} ({columnList}) values ({parameterList})\";\r\n        connection.Execute(cmd, entityToInsert, transaction, commandTimeout);\r\n\r\n        var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();\r\n        var keyName = propertyInfos[0].Name;\r\n        var r = connection.Query($\"SELECT FIRST 1 {keyName} ID FROM {tableName} ORDER BY {keyName} DESC\", transaction: transaction, commandTimeout: commandTimeout);\r\n\r\n        var id = r.First().ID;\r\n        if (id == null) return 0;\r\n        if (propertyInfos.Length == 0) return Convert.ToInt32(id);\r\n\r\n        var idp = propertyInfos[0];\r\n        idp.SetValue(entityToInsert, Convert.ChangeType(id, idp.PropertyType), null);\r\n\r\n        return Convert.ToInt32(id);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds the name of a column.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnName(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"{0}\", columnName);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Adds a column equality to a parameter.\r\n    /// </summary>\r\n    /// <param name=\"sb\">The string builder  to append to.</param>\r\n    /// <param name=\"columnName\">The column name.</param>\r\n    public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)\r\n    {\r\n        sb.AppendFormat(\"{0} = @{1}\", columnName, columnName);\r\n    }\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <AssemblyName>Dapper.Tests.Contrib</AssemblyName>\n    <Description>Dapper Contrib Test Suite</Description>\n    <TargetFrameworks>netcoreapp3.1;net462;net5.0</TargetFrameworks>\n    <NoWarn>$(NoWarn);CA1816;IDE0063;xUnit1004</NoWarn>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Remove=\"Test.DB.sdf\" />\n    <None Update=\"xunit.runner.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Dapper.SqlBuilder\" Version=\"2.0.78\" />\n    <PackageReference Include=\"Microsoft.Data.Sqlite\" Version=\"5.0.0\" />\n    <PackageReference Include=\"MySqlConnector\" Version=\"1.1.0\" />\n    <PackageReference Include=\"System.Data.SqlClient\" Version=\"4.8.2\" />\n  </ItemGroup>\n  <ItemGroup Condition=\" '$(TargetFramework)' == 'net462' \">\n    <Reference Include=\"Microsoft.CSharp\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/Helpers/Attributes.cs",
    "content": "﻿using System;\r\nusing Xunit.Sdk;\r\n\r\nnamespace Dapper.Tests\r\n{\r\n    /// <summary>\r\n    /// <para>Override for <see cref=\"Xunit.FactAttribute\"/> that truncates our DisplayName down.</para>\r\n    /// <para>\r\n    /// Attribute that is applied to a method to indicate that it is a fact that should\r\n    /// be run by the test runner. It can also be extended to support a customized definition\r\n    /// of a test method.\r\n    /// </para>\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\r\n    [XunitTestCaseDiscoverer(\"Dapper.Tests.FactDiscoverer\", \"Dapper.Tests.Contrib\")]\r\n    public class FactAttribute : Xunit.FactAttribute\r\n    {\r\n    }\r\n\r\n    /// <summary>\r\n    /// <para>Override for <see cref=\"Xunit.TheoryAttribute\"/> that truncates our DisplayName down.</para>\r\n    /// <para>\r\n    /// Marks a test method as being a data theory. Data theories are tests which are\r\n    /// fed various bits of data from a data source, mapping to parameters on the test\r\n    /// method. If the data source contains multiple rows, then the test method is executed\r\n    /// multiple times (once with each data row). Data is provided by attributes which\r\n    /// derive from Xunit.Sdk.DataAttribute (notably, Xunit.InlineDataAttribute and Xunit.MemberDataAttribute).\r\n    /// </para>\r\n    /// </summary>\r\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\r\n    [XunitTestCaseDiscoverer(\"Dapper.Tests.TheoryDiscoverer\", \"Dapper.Tests.Contrib\")]\r\n    public class TheoryAttribute : Xunit.TheoryAttribute { }\r\n\r\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\r\n    public sealed class FactLongRunningAttribute : FactAttribute\r\n    {\r\n        public FactLongRunningAttribute()\r\n        {\r\n#if !LONG_RUNNING\r\n            Skip = \"Long running\";\r\n#endif\r\n        }\r\n\r\n        public string Url { get; private set; }\r\n    }\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/Helpers/XunitSkippable.cs",
    "content": "﻿using System;\r\n\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing Xunit.Abstractions;\r\nusing Xunit.Sdk;\r\n\r\nnamespace Dapper.Tests\r\n{\r\n    public static class Skip\r\n    {\r\n        public static void Inconclusive(string reason = \"inconclusive\")\r\n            => throw new SkipTestException(reason);\r\n\r\n        public static void If<T>(object obj, string reason = null)\r\n            where T : class\r\n        {\r\n            if (obj is T) Skip.Inconclusive(reason ?? $\"not valid for {typeof(T).FullName}\");\r\n        }\r\n    }\r\n\r\n#pragma warning disable RCS1194 // Implement exception constructors.\r\n    public class SkipTestException : Exception\r\n    {\r\n        public SkipTestException(string reason) : base(reason)\r\n        {\r\n        }\r\n    }\r\n#pragma warning restore RCS1194 // Implement exception constructors.\r\n\r\n    public class FactDiscoverer : Xunit.Sdk.FactDiscoverer\r\n    {\r\n        public FactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { }\r\n\r\n        protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)\r\n            => new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod);\r\n    }\r\n\r\n    public class TheoryDiscoverer : Xunit.Sdk.TheoryDiscoverer\r\n    {\r\n        public TheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { }\r\n\r\n        protected override IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow)\r\n            => new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, dataRow) };\r\n\r\n        protected override IEnumerable<IXunitTestCase> CreateTestCasesForSkip(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, string skipReason)\r\n            => new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) };\r\n\r\n        protected override IEnumerable<IXunitTestCase> CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute)\r\n            => new[] { new SkippableTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) };\r\n\r\n        protected override IEnumerable<IXunitTestCase> CreateTestCasesForSkippedDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow, string skipReason)\r\n            => new[] { new NamedSkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, skipReason, dataRow) };\r\n    }\r\n\r\n    public class SkippableTestCase : XunitTestCase\r\n    {\r\n        protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>\r\n            base.GetDisplayName(factAttribute, displayName).StripName();\r\n\r\n        [Obsolete(\"Called by the de-serializer; should only be called by deriving classes for de-serialization purposes\")]\r\n        public SkippableTestCase() { }\r\n\r\n        public SkippableTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, object[] testMethodArguments = null)\r\n            : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments)\r\n        {\r\n        }\r\n\r\n        public override async Task<RunSummary> RunAsync(\r\n            IMessageSink diagnosticMessageSink,\r\n            IMessageBus messageBus,\r\n            object[] constructorArguments,\r\n            ExceptionAggregator aggregator,\r\n            CancellationTokenSource cancellationTokenSource)\r\n        {\r\n            var skipMessageBus = new SkippableMessageBus(messageBus);\r\n            var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false);\r\n            return result.Update(skipMessageBus);\r\n        }\r\n    }\r\n\r\n    public class SkippableTheoryTestCase : XunitTheoryTestCase\r\n    {\r\n        protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>\r\n            base.GetDisplayName(factAttribute, displayName).StripName();\r\n\r\n        [Obsolete(\"Called by the de-serializer; should only be called by deriving classes for de-serialization purposes\")]\r\n        public SkippableTheoryTestCase() { }\r\n\r\n        public SkippableTheoryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod)\r\n            : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) { }\r\n\r\n        public override async Task<RunSummary> RunAsync(\r\n            IMessageSink diagnosticMessageSink,\r\n            IMessageBus messageBus,\r\n            object[] constructorArguments,\r\n            ExceptionAggregator aggregator,\r\n            CancellationTokenSource cancellationTokenSource)\r\n        {\r\n            var skipMessageBus = new SkippableMessageBus(messageBus);\r\n            var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false);\r\n            return result.Update(skipMessageBus);\r\n        }\r\n    }\r\n\r\n    public class NamedSkippedDataRowTestCase : XunitSkippedDataRowTestCase\r\n    {\r\n        protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>\r\n            base.GetDisplayName(factAttribute, displayName).StripName();\r\n\r\n        [Obsolete(\"Called by the de-serializer; should only be called by deriving classes for de-serialization purposes\")]\r\n        public NamedSkippedDataRowTestCase() { }\r\n\r\n        public NamedSkippedDataRowTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, string skipReason, object[] testMethodArguments = null)\r\n        : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, skipReason, testMethodArguments) { }\r\n    }\r\n\r\n    public class SkippableMessageBus : IMessageBus\r\n    {\r\n        private readonly IMessageBus InnerBus;\r\n        public SkippableMessageBus(IMessageBus innerBus) => InnerBus = innerBus;\r\n\r\n        public int DynamicallySkippedTestCount { get; private set; }\r\n\r\n        public void Dispose() { }\r\n\r\n        public bool QueueMessage(IMessageSinkMessage message)\r\n        {\r\n            if (message is ITestFailed testFailed)\r\n            {\r\n                var exceptionType = testFailed.ExceptionTypes.FirstOrDefault();\r\n                if (exceptionType == typeof(SkipTestException).FullName)\r\n                {\r\n                    DynamicallySkippedTestCount++;\r\n                    return InnerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages.FirstOrDefault()));\r\n                }\r\n            }\r\n            return InnerBus.QueueMessage(message);\r\n        }\r\n    }\r\n\r\n    internal static class XUnitExtensions\r\n    {\r\n        internal static string StripName(this string name) =>\r\n            name.Replace(\"Dapper.Tests.\", \"\");\r\n\r\n        public static RunSummary Update(this RunSummary summary, SkippableMessageBus bus)\r\n        {\r\n            if (bus.DynamicallySkippedTestCount > 0)\r\n            {\r\n                summary.Failed -= bus.DynamicallySkippedTestCount;\r\n                summary.Skipped += bus.DynamicallySkippedTestCount;\r\n            }\r\n            return summary;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/TestSuite.Async.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\n\r\nusing Dapper.Contrib.Extensions;\r\nusing FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute;\r\nusing Xunit;\r\n\r\nnamespace Dapper.Tests.Contrib\r\n{\r\n    public abstract partial class TestSuite\r\n    {\r\n        [Fact]\r\n        public async Task TypeWithGenericParameterCanBeInsertedAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<GenericType<string>>();\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                await connection.InsertAsync(objectToInsert);\r\n\r\n                Assert.Single(connection.GetAll<GenericType<string>>());\r\n\r\n                var objectsToInsert = new List<GenericType<string>>(2)\r\n                {\r\n                    new GenericType<string>\r\n                    {\r\n                        Id = Guid.NewGuid().ToString(),\r\n                        Name = \"1\",\r\n                    },\r\n                    new GenericType<string>\r\n                    {\r\n                        Id = Guid.NewGuid().ToString(),\r\n                        Name = \"2\",\r\n                    }\r\n                };\r\n\r\n                await connection.InsertAsync(objectsToInsert);\r\n                var list = connection.GetAll<GenericType<string>>();\r\n                Assert.Equal(3, list.Count());\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task TypeWithGenericParameterCanBeUpdatedAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                await connection.InsertAsync(objectToInsert);\r\n\r\n                objectToInsert.Name = \"somethingelse\";\r\n                await connection.UpdateAsync(objectToInsert);\r\n\r\n                var updatedObject = connection.Get<GenericType<string>>(objectToInsert.Id);\r\n                Assert.Equal(objectToInsert.Name, updatedObject.Name);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task TypeWithGenericParameterCanBeDeletedAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                await connection.InsertAsync(objectToInsert);\r\n\r\n                bool deleted = await connection.DeleteAsync(objectToInsert);\r\n                Assert.True(deleted);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task GetAsyncSucceedsAfterDeleteAsyncWhenExplicitKeyPresent()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAsync(new ObjectX { ObjectXId = Guid.NewGuid().ToString() }).ConfigureAwait(false);\r\n                var retrieved = await connection.GetAsync<ObjectX>(Guid.NewGuid().ToString()).ConfigureAwait(false);\r\n                Assert.Null(retrieved);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Tests for issue #351 \r\n        /// </summary>\r\n        [Fact]\r\n        public async Task InsertGetUpdateDeleteWithExplicitKeyAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var guid = Guid.NewGuid().ToString();\r\n                var o1 = new ObjectX { ObjectXId = guid, Name = \"Foo\" };\r\n                var originalxCount = (await connection.QueryAsync<int>(\"Select Count(*) From ObjectX\").ConfigureAwait(false)).First();\r\n                await connection.InsertAsync(o1).ConfigureAwait(false);\r\n                var list1 = (await connection.QueryAsync<ObjectX>(\"select * from ObjectX\").ConfigureAwait(false)).ToList();\r\n                Assert.Equal(list1.Count, originalxCount + 1);\r\n                o1 = await connection.GetAsync<ObjectX>(guid).ConfigureAwait(false);\r\n                Assert.Equal(o1.ObjectXId, guid);\r\n                o1.Name = \"Bar\";\r\n                await connection.UpdateAsync(o1).ConfigureAwait(false);\r\n                o1 = await connection.GetAsync<ObjectX>(guid).ConfigureAwait(false);\r\n                Assert.Equal(\"Bar\", o1.Name);\r\n                await connection.DeleteAsync(o1).ConfigureAwait(false);\r\n                o1 = await connection.GetAsync<ObjectX>(guid).ConfigureAwait(false);\r\n                Assert.Null(o1);\r\n\r\n                const int id = 42;\r\n                var o2 = new ObjectY { ObjectYId = id, Name = \"Foo\" };\r\n                var originalyCount = connection.Query<int>(\"Select Count(*) From ObjectY\").First();\r\n                await connection.InsertAsync(o2).ConfigureAwait(false);\r\n                var list2 = (await connection.QueryAsync<ObjectY>(\"select * from ObjectY\").ConfigureAwait(false)).ToList();\r\n                Assert.Equal(list2.Count, originalyCount + 1);\r\n                o2 = await connection.GetAsync<ObjectY>(id).ConfigureAwait(false);\r\n                Assert.Equal(o2.ObjectYId, id);\r\n                o2.Name = \"Bar\";\r\n                await connection.UpdateAsync(o2).ConfigureAwait(false);\r\n                o2 = await connection.GetAsync<ObjectY>(id).ConfigureAwait(false);\r\n                Assert.Equal(\"Bar\", o2.Name);\r\n                await connection.DeleteAsync(o2).ConfigureAwait(false);\r\n                o2 = await connection.GetAsync<ObjectY>(id).ConfigureAwait(false);\r\n                Assert.Null(o2);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task TableNameAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                // tests against \"Automobiles\" table (Table attribute)\r\n                var id = await connection.InsertAsync(new Car { Name = \"VolvoAsync\" }).ConfigureAwait(false);\r\n                var car = await connection.GetAsync<Car>(id).ConfigureAwait(false);\r\n                Assert.NotNull(car);\r\n                Assert.Equal(\"VolvoAsync\", car.Name);\r\n                Assert.True(await connection.UpdateAsync(new Car { Id = id, Name = \"SaabAsync\" }).ConfigureAwait(false));\r\n                Assert.Equal(\"SaabAsync\", (await connection.GetAsync<Car>(id).ConfigureAwait(false)).Name);\r\n                Assert.True(await connection.DeleteAsync(new Car { Id = id }).ConfigureAwait(false));\r\n                Assert.Null(await connection.GetAsync<Car>(id).ConfigureAwait(false));\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task TestSimpleGetAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id = await connection.InsertAsync(new User { Name = \"Adama\", Age = 10 }).ConfigureAwait(false);\r\n                var user = await connection.GetAsync<User>(id).ConfigureAwait(false);\r\n                Assert.Equal(id, user.Id);\r\n                Assert.Equal(\"Adama\", user.Name);\r\n                await connection.DeleteAsync(user).ConfigureAwait(false);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertGetUpdateAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                Assert.Null(await connection.GetAsync<User>(30).ConfigureAwait(false));\r\n\r\n                var originalCount = (await connection.QueryAsync<int>(\"select Count(*) from Users\").ConfigureAwait(false)).First();\r\n\r\n                var id = await connection.InsertAsync(new User { Name = \"Adam\", Age = 10 }).ConfigureAwait(false);\r\n\r\n                //get a user with \"isdirty\" tracking\r\n                var user = await connection.GetAsync<IUser>(id).ConfigureAwait(false);\r\n                Assert.Equal(\"Adam\", user.Name);\r\n                Assert.False(await connection.UpdateAsync(user).ConfigureAwait(false)); //returns false if not updated, based on tracking\r\n                user.Name = \"Bob\";\r\n                Assert.True(await connection.UpdateAsync(user).ConfigureAwait(false)); //returns true if updated, based on tracking\r\n                user = await connection.GetAsync<IUser>(id).ConfigureAwait(false);\r\n                Assert.Equal(\"Bob\", user.Name);\r\n\r\n                //get a user with no tracking\r\n                var notrackedUser = await connection.GetAsync<User>(id).ConfigureAwait(false);\r\n                Assert.Equal(\"Bob\", notrackedUser.Name);\r\n                Assert.True(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false));\r\n                //returns true, even though user was not changed\r\n                notrackedUser.Name = \"Cecil\";\r\n                Assert.True(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false));\r\n                Assert.Equal(\"Cecil\", (await connection.GetAsync<User>(id).ConfigureAwait(false)).Name);\r\n\r\n                Assert.Equal((await connection.QueryAsync<User>(\"select * from Users\").ConfigureAwait(false)).Count(), originalCount + 1);\r\n                Assert.True(await connection.DeleteAsync(user).ConfigureAwait(false));\r\n                Assert.Equal((await connection.QueryAsync<User>(\"select * from Users\").ConfigureAwait(false)).Count(), originalCount);\r\n\r\n                Assert.False(await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)); //returns false, user not found\r\n\r\n                Assert.True(await connection.InsertAsync(new User { Name = \"Adam\", Age = 10 }).ConfigureAwait(false) > originalCount + 1);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertCheckKeyAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                Assert.Null(await connection.GetAsync<IUser>(3).ConfigureAwait(false));\r\n                var user = new User { Name = \"Adamb\", Age = 10 };\r\n                var id = await connection.InsertAsync(user).ConfigureAwait(false);\r\n                Assert.Equal(user.Id, id);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task BuilderSelectClauseAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var rand = new Random(8675309);\r\n                var data = new List<User>(100);\r\n                for (var i = 0; i < 100; i++)\r\n                {\r\n                    var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };\r\n                    data.Add(nU);\r\n                    nU.Id = await connection.InsertAsync(nU).ConfigureAwait(false);\r\n                }\r\n\r\n                var builder = new SqlBuilder();\r\n                var justId = builder.AddTemplate(\"SELECT /**select**/ FROM Users\");\r\n                var all = builder.AddTemplate(\"SELECT Name, /**select**/, Age FROM Users\");\r\n\r\n                builder.Select(\"Id\");\r\n\r\n                var ids = await connection.QueryAsync<int>(justId.RawSql, justId.Parameters).ConfigureAwait(false);\r\n                var users = await connection.QueryAsync<User>(all.RawSql, all.Parameters).ConfigureAwait(false);\r\n\r\n                foreach (var u in data)\r\n                {\r\n                    if (!ids.Any(i => u.Id == i)) throw new Exception(\"Missing ids in select\");\r\n                    if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age))\r\n                        throw new Exception(\"Missing users in select\");\r\n                }\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task BuilderTemplateWithoutCompositionAsync()\r\n        {\r\n            var builder = new SqlBuilder();\r\n            var template = builder.AddTemplate(\"SELECT COUNT(*) FROM Users WHERE Age = @age\", new { age = 5 });\r\n\r\n            if (template.RawSql == null) throw new Exception(\"RawSql null\");\r\n            if (template.Parameters == null) throw new Exception(\"Parameters null\");\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                await connection.InsertAsync(new User { Age = 5, Name = \"Testy McTestington\" }).ConfigureAwait(false);\r\n\r\n                if ((await connection.QueryAsync<int>(template.RawSql, template.Parameters).ConfigureAwait(false)).Single() != 1)\r\n                    throw new Exception(\"Query failed\");\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertEnumerableAsync()\r\n        {\r\n            await InsertHelperAsync(src => src.AsEnumerable()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertArrayAsync()\r\n        {\r\n            await InsertHelperAsync(src => src.ToArray()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertListAsync()\r\n        {\r\n            await InsertHelperAsync(src => src.ToList()).ConfigureAwait(false);\r\n        }\r\n\r\n        private async Task InsertHelperAsync<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false);\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task UpdateEnumerableAsync()\r\n        {\r\n            await UpdateHelperAsync(src => src.AsEnumerable()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task UpdateArrayAsync()\r\n        {\r\n            await UpdateHelperAsync(src => src.ToArray()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task UpdateListAsync()\r\n        {\r\n            await UpdateHelperAsync(src => src.ToList()).ConfigureAwait(false);\r\n        }\r\n\r\n        private async Task UpdateHelperAsync<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false);\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n                foreach (var user in users)\r\n                {\r\n                    user.Name += \" updated\";\r\n                }\r\n                await connection.UpdateAsync(helper(users)).ConfigureAwait(false);\r\n                var name = connection.Query<User>(\"select * from Users\").First().Name;\r\n                Assert.Contains(\"updated\", name);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task DeleteEnumerableAsync()\r\n        {\r\n            await DeleteHelperAsync(src => src.AsEnumerable()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task DeleteArrayAsync()\r\n        {\r\n            await DeleteHelperAsync(src => src.ToArray()).ConfigureAwait(false);\r\n        }\r\n\r\n        [Fact]\r\n        public async Task DeleteListAsync()\r\n        {\r\n            await DeleteHelperAsync(src => src.ToList()).ConfigureAwait(false);\r\n        }\r\n\r\n        private async Task DeleteHelperAsync<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false);\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n\r\n                var usersToDelete = users.Take(10).ToList();\r\n                await connection.DeleteAsync(helper(usersToDelete)).ConfigureAwait(false);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities - 10);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task GetAllAsync()\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var total = await connection.InsertAsync(users).ConfigureAwait(false);\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = (List<User>)await connection.GetAllAsync<User>().ConfigureAwait(false);\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n                var iusers = await connection.GetAllAsync<IUser>().ConfigureAwait(false);\r\n                Assert.Equal(iusers.ToList().Count, numberOfEntities);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Test for issue #933\r\n        /// </summary>\r\n        [Fact]\r\n        public async void GetAsyncAndGetAllAsyncWithNullableValues()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id1 = connection.Insert(new NullableDate { DateValue = new DateTime(2011, 07, 14) });\r\n                var id2 = connection.Insert(new NullableDate { DateValue = null });\r\n\r\n                var value1 = await connection.GetAsync<INullableDate>(id1).ConfigureAwait(false);\r\n                Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value);\r\n\r\n                var value2 = await connection.GetAsync<INullableDate>(id2).ConfigureAwait(false);\r\n                Assert.True(value2.DateValue == null);\r\n\r\n                var value3 = await connection.GetAllAsync<INullableDate>().ConfigureAwait(false);\r\n                var valuesList = value3.ToList();\r\n                Assert.Equal(new DateTime(2011, 07, 14), valuesList[0].DateValue.Value);\r\n                Assert.True(valuesList[1].DateValue == null);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task InsertFieldWithReservedNameAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n                var id = await connection.InsertAsync(new Result { Name = \"Adam\", Order = 1 }).ConfigureAwait(false);\r\n\r\n                var result = await connection.GetAsync<Result>(id).ConfigureAwait(false);\r\n                Assert.Equal(1, result.Order);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public async Task DeleteAllAsync()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n\r\n                var id1 = await connection.InsertAsync(new User { Name = \"Alice\", Age = 32 }).ConfigureAwait(false);\r\n                var id2 = await connection.InsertAsync(new User { Name = \"Bob\", Age = 33 }).ConfigureAwait(false);\r\n                await connection.DeleteAllAsync<User>().ConfigureAwait(false);\r\n                Assert.Null(await connection.GetAsync<User>(id1).ConfigureAwait(false));\r\n                Assert.Null(await connection.GetAsync<User>(id2).ConfigureAwait(false));\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/TestSuite.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Data;\r\nusing System.Linq;\r\nusing Dapper.Contrib.Extensions;\r\nusing Xunit;\r\n\r\nusing FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute;\r\n\r\nnamespace Dapper.Tests.Contrib\r\n{\r\n    [Table(\"ObjectX\")]\r\n    public class ObjectX\r\n    {\r\n        [ExplicitKey]\r\n        public string ObjectXId { get; set; }\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    [Table(\"ObjectY\")]\r\n    public class ObjectY\r\n    {\r\n        [ExplicitKey]\r\n        public int ObjectYId { get; set; }\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    [Table(\"ObjectZ\")]\r\n    public class ObjectZ\r\n    {\r\n        [ExplicitKey]\r\n        public int Id { get; set; }\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    public interface IUser\r\n    {\r\n        [Key]\r\n        int Id { get; set; }\r\n        string Name { get; set; }\r\n        int Age { get; set; }\r\n    }\r\n\r\n    public class User : IUser\r\n    {\r\n        public int Id { get; set; }\r\n        public string Name { get; set; }\r\n        public int Age { get; set; }\r\n    }\r\n\r\n    public interface INullableDate\r\n    {\r\n        [Key]\r\n        int Id { get; set; }\r\n        DateTime? DateValue { get; set; }\r\n    }\r\n\r\n    public class NullableDate : INullableDate\r\n    {\r\n        public int Id { get; set; }\r\n        public DateTime? DateValue { get; set; }\r\n    }\r\n\r\n    public class Person\r\n    {\r\n        public int Id { get; set; }\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    [Table(\"Stuff\")]\r\n    public class Stuff\r\n    {\r\n        [Key]\r\n        public short TheId { get; set; }\r\n        public string Name { get; set; }\r\n        public DateTime? Created { get; set; }\r\n    }\r\n\r\n    [Table(\"Automobiles\")]\r\n    public class Car\r\n    {\r\n        public int Id { get; set; }\r\n        public string Name { get; set; }\r\n        [Computed]\r\n        public string Computed { get; set; }\r\n    }\r\n\r\n    [Table(\"Results\")]\r\n    public class Result\r\n    {\r\n        public int Id { get; set; }\r\n        public string Name { get; set; }\r\n        public int Order { get; set; }\r\n    }\r\n\r\n    [Table(\"GenericType\")]\r\n    public class GenericType<T>\r\n    {\r\n        [ExplicitKey]\r\n        public string Id { get; set; }\r\n        public string Name { get; set; }\r\n    }\r\n\r\n    public abstract partial class TestSuite\r\n    {\r\n        public abstract IDbConnection GetConnection();\r\n\r\n        protected static string GetConnectionString(string name, string defaultConnectionString) =>\r\n            Environment.GetEnvironmentVariable(name) ?? defaultConnectionString;\r\n\r\n        private IDbConnection GetOpenConnection()\r\n        {\r\n            var connection = GetConnection();\r\n            connection.Open();\r\n            return connection;\r\n        }\r\n\r\n        [Fact]\r\n        public void TypeWithGenericParameterCanBeInserted()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<GenericType<string>>();\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                connection.Insert(objectToInsert);\r\n\r\n                Assert.Single(connection.GetAll<GenericType<string>>());\r\n\r\n                var objectsToInsert = new List<GenericType<string>>(2)\r\n                {\r\n                    new GenericType<string>\r\n                    {\r\n                        Id = Guid.NewGuid().ToString(),\r\n                        Name = \"1\",\r\n                    },\r\n                    new GenericType<string>\r\n                    {\r\n                        Id = Guid.NewGuid().ToString(),\r\n                        Name = \"2\",\r\n                    }\r\n                };\r\n\r\n                connection.Insert(objectsToInsert);\r\n                var list = connection.GetAll<GenericType<string>>();\r\n                Assert.Equal(3, list.Count());\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void TypeWithGenericParameterCanBeUpdated()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                connection.Insert(objectToInsert);\r\n\r\n                objectToInsert.Name = \"somethingelse\";\r\n                connection.Update(objectToInsert);\r\n\r\n                var updatedObject = connection.Get<GenericType<string>>(objectToInsert.Id);\r\n                Assert.Equal(objectToInsert.Name, updatedObject.Name);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void TypeWithGenericParameterCanBeDeleted()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var objectToInsert = new GenericType<string>\r\n                {\r\n                    Id = Guid.NewGuid().ToString(),\r\n                    Name = \"something\"\r\n                };\r\n                connection.Insert(objectToInsert);\r\n\r\n                bool deleted = connection.Delete(objectToInsert);\r\n                Assert.True(deleted);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void Issue418()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                //update first (will fail) then insert\r\n                //added for bug #418\r\n                var updateObject = new ObjectX\r\n                {\r\n                    ObjectXId = Guid.NewGuid().ToString(),\r\n                    Name = \"Someone\"\r\n                };\r\n                var updates = connection.Update(updateObject);\r\n                Assert.False(updates);\r\n\r\n                connection.DeleteAll<ObjectX>();\r\n\r\n                var objectXId = Guid.NewGuid().ToString();\r\n                var insertObject = new ObjectX\r\n                {\r\n                    ObjectXId = objectXId,\r\n                    Name = \"Someone else\"\r\n                };\r\n                connection.Insert(insertObject);\r\n                var list = connection.GetAll<ObjectX>();\r\n                Assert.Single(list);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Tests for issue #351 \r\n        /// </summary>\r\n        [Fact]\r\n        public void InsertGetUpdateDeleteWithExplicitKey()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var guid = Guid.NewGuid().ToString();\r\n                var o1 = new ObjectX { ObjectXId = guid, Name = \"Foo\" };\r\n                var originalxCount = connection.Query<int>(\"Select Count(*) From ObjectX\").First();\r\n                connection.Insert(o1);\r\n                var list1 = connection.Query<ObjectX>(\"select * from ObjectX\").ToList();\r\n                Assert.Equal(list1.Count, originalxCount + 1);\r\n                o1 = connection.Get<ObjectX>(guid);\r\n                Assert.Equal(o1.ObjectXId, guid);\r\n                o1.Name = \"Bar\";\r\n                connection.Update(o1);\r\n                o1 = connection.Get<ObjectX>(guid);\r\n                Assert.Equal(\"Bar\", o1.Name);\r\n                connection.Delete(o1);\r\n                o1 = connection.Get<ObjectX>(guid);\r\n                Assert.Null(o1);\r\n\r\n                const int id = 42;\r\n                var o2 = new ObjectY { ObjectYId = id, Name = \"Foo\" };\r\n                var originalyCount = connection.Query<int>(\"Select Count(*) From ObjectY\").First();\r\n                connection.Insert(o2);\r\n                var list2 = connection.Query<ObjectY>(\"select * from ObjectY\").ToList();\r\n                Assert.Equal(list2.Count, originalyCount + 1);\r\n                o2 = connection.Get<ObjectY>(id);\r\n                Assert.Equal(o2.ObjectYId, id);\r\n                o2.Name = \"Bar\";\r\n                connection.Update(o2);\r\n                o2 = connection.Get<ObjectY>(id);\r\n                Assert.Equal(\"Bar\", o2.Name);\r\n                connection.Delete(o2);\r\n                o2 = connection.Get<ObjectY>(id);\r\n                Assert.Null(o2);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void GetAllWithExplicitKey()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var guid = Guid.NewGuid().ToString();\r\n                var o1 = new ObjectX { ObjectXId = guid, Name = \"Foo\" };\r\n                connection.Insert(o1);\r\n\r\n                var objectXs = connection.GetAll<ObjectX>().ToList();\r\n                Assert.True(objectXs.Count > 0);\r\n                Assert.Equal(1, objectXs.Count(x => x.ObjectXId == guid));\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertGetUpdateDeleteWithExplicitKeyNamedId()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                const int id = 42;\r\n                var o2 = new ObjectZ { Id = id, Name = \"Foo\" };\r\n                connection.Insert(o2);\r\n                var list2 = connection.Query<ObjectZ>(\"select * from ObjectZ\").ToList();\r\n                Assert.Single(list2);\r\n                o2 = connection.Get<ObjectZ>(id);\r\n                Assert.Equal(o2.Id, id);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void ShortIdentity()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                const string name = \"First item\";\r\n                var id = connection.Insert(new Stuff { Name = name });\r\n                Assert.True(id > 0); // 1-n are valid here, due to parallel tests\r\n                var item = connection.Get<Stuff>(id);\r\n                Assert.Equal(item.TheId, (short)id);\r\n                Assert.Equal(item.Name, name);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void NullDateTime()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.Insert(new Stuff { Name = \"First item\" });\r\n                connection.Insert(new Stuff { Name = \"Second item\", Created = DateTime.Now });\r\n                var stuff = connection.Query<Stuff>(\"select * from Stuff\").ToList();\r\n                Assert.Null(stuff[0].Created);\r\n                Assert.NotNull(stuff.Last().Created);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void TableName()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                // tests against \"Automobiles\" table (Table attribute)\r\n                var id = connection.Insert(new Car { Name = \"Volvo\" });\r\n                var car = connection.Get<Car>(id);\r\n                Assert.NotNull(car);\r\n                Assert.Equal(\"Volvo\", car.Name);\r\n                Assert.Equal(\"Volvo\", connection.Get<Car>(id).Name);\r\n                Assert.True(connection.Update(new Car { Id = (int)id, Name = \"Saab\" }));\r\n                Assert.Equal(\"Saab\", connection.Get<Car>(id).Name);\r\n                Assert.True(connection.Delete(new Car { Id = (int)id }));\r\n                Assert.Null(connection.Get<Car>(id));\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void TestSimpleGet()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id = connection.Insert(new User { Name = \"Adama\", Age = 10 });\r\n                var user = connection.Get<User>(id);\r\n                Assert.Equal(user.Id, (int)id);\r\n                Assert.Equal(\"Adama\", user.Name);\r\n                connection.Delete(user);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void TestClosedConnection()\r\n        {\r\n            using (var connection = GetConnection())\r\n            {\r\n                Assert.True(connection.Insert(new User { Name = \"Adama\", Age = 10 }) > 0);\r\n                var users = connection.GetAll<User>();\r\n                Assert.NotEmpty(users);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertEnumerable()\r\n        {\r\n            InsertHelper(src => src.AsEnumerable());\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertArray()\r\n        {\r\n            InsertHelper(src => src.ToArray());\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertList()\r\n        {\r\n            InsertHelper(src => src.ToList());\r\n        }\r\n\r\n        private void InsertHelper<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n\r\n                var total = connection.Insert(helper(users));\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void UpdateEnumerable()\r\n        {\r\n            UpdateHelper(src => src.AsEnumerable());\r\n        }\r\n\r\n        [Fact]\r\n        public void UpdateArray()\r\n        {\r\n            UpdateHelper(src => src.ToArray());\r\n        }\r\n\r\n        [Fact]\r\n        public void UpdateList()\r\n        {\r\n            UpdateHelper(src => src.ToList());\r\n        }\r\n\r\n        private void UpdateHelper<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n\r\n                var total = connection.Insert(helper(users));\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n                foreach (var user in users)\r\n                {\r\n                    user.Name += \" updated\";\r\n                }\r\n                connection.Update(helper(users));\r\n                var name = connection.Query<User>(\"select * from Users\").First().Name;\r\n                Assert.Contains(\"updated\", name);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void DeleteEnumerable()\r\n        {\r\n            DeleteHelper(src => src.AsEnumerable());\r\n        }\r\n\r\n        [Fact]\r\n        public void DeleteArray()\r\n        {\r\n            DeleteHelper(src => src.ToArray());\r\n        }\r\n\r\n        [Fact]\r\n        public void DeleteList()\r\n        {\r\n            DeleteHelper(src => src.ToList());\r\n        }\r\n\r\n        private void DeleteHelper<T>(Func<IEnumerable<User>, T> helper)\r\n            where T : class\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n\r\n                var total = connection.Insert(helper(users));\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n\r\n                var usersToDelete = users.Take(10).ToList();\r\n                connection.Delete(helper(usersToDelete));\r\n                users = connection.Query<User>(\"select * from Users\").ToList();\r\n                Assert.Equal(users.Count, numberOfEntities - 10);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertGetUpdate()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n                Assert.Null(connection.Get<User>(3));\r\n\r\n                //insert with computed attribute that should be ignored\r\n                connection.Insert(new Car { Name = \"Volvo\", Computed = \"this property should be ignored\" });\r\n\r\n                var id = connection.Insert(new User { Name = \"Adam\", Age = 10 });\r\n\r\n                //get a user with \"isdirty\" tracking\r\n                var user = connection.Get<IUser>(id);\r\n                Assert.Equal(\"Adam\", user.Name);\r\n                Assert.False(connection.Update(user));    //returns false if not updated, based on tracking\r\n                user.Name = \"Bob\";\r\n                Assert.True(connection.Update(user));    //returns true if updated, based on tracking\r\n                user = connection.Get<IUser>(id);\r\n                Assert.Equal(\"Bob\", user.Name);\r\n\r\n                //get a user with no tracking\r\n                var notrackedUser = connection.Get<User>(id);\r\n                Assert.Equal(\"Bob\", notrackedUser.Name);\r\n                Assert.True(connection.Update(notrackedUser));   //returns true, even though user was not changed\r\n                notrackedUser.Name = \"Cecil\";\r\n                Assert.True(connection.Update(notrackedUser));\r\n                Assert.Equal(\"Cecil\", connection.Get<User>(id).Name);\r\n\r\n                Assert.Single(connection.Query<User>(\"select * from Users\"));\r\n                Assert.True(connection.Delete(user));\r\n                Assert.Empty(connection.Query<User>(\"select * from Users\"));\r\n\r\n                Assert.False(connection.Update(notrackedUser));   //returns false, user not found\r\n            }\r\n        }\r\n\r\n#if SQLCE\r\n        [Fact(Skip = \"Not parallel friendly - thinking about how to test this\")]\r\n        public void InsertWithCustomDbType()\r\n        {\r\n            SqlMapperExtensions.GetDatabaseType = conn => \"SQLiteConnection\";\r\n\r\n            bool sqliteCodeCalled = false;\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n                Assert.IsNull(connection.Get<User>(3));\r\n                try\r\n                {\r\n                    connection.Insert(new User { Name = \"Adam\", Age = 10 });\r\n                }\r\n                catch (SqlCeException ex)\r\n                {\r\n                    sqliteCodeCalled = ex.Message.IndexOf(\"There was an error parsing the query\", StringComparison.OrdinalIgnoreCase) >= 0;\r\n                }\r\n                // ReSharper disable once EmptyGeneralCatchClause\r\n                catch (Exception)\r\n                {\r\n                }\r\n            }\r\n            SqlMapperExtensions.GetDatabaseType = null;\r\n\r\n            if (!sqliteCodeCalled)\r\n            {\r\n                throw new Exception(\"Was expecting sqlite code to be called\");\r\n            }\r\n        }\r\n#endif\r\n\r\n        [Fact]\r\n        public void InsertWithCustomTableNameMapper()\r\n        {\r\n            SqlMapperExtensions.TableNameMapper = type =>\r\n            {\r\n                switch (type.Name)\r\n                {\r\n                    case \"Person\":\r\n                        return \"People\";\r\n                    default:\r\n                        var tableattr = type.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == \"TableAttribute\") as dynamic;\r\n                        if (tableattr != null)\r\n                            return tableattr.Name;\r\n\r\n                        var name = type.Name + \"s\";\r\n                        if (type.IsInterface && name.StartsWith(\"I\"))\r\n                            return name.Substring(1);\r\n                        return name;\r\n                }\r\n            };\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id = connection.Insert(new Person { Name = \"Mr Mapper\" });\r\n                Assert.Equal(1, id);\r\n                connection.GetAll<Person>();\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void GetAll()\r\n        {\r\n            const int numberOfEntities = 10;\r\n\r\n            var users = new List<User>(numberOfEntities);\r\n            for (var i = 0; i < numberOfEntities; i++)\r\n                users.Add(new User { Name = \"User \" + i, Age = i });\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n\r\n                var total = connection.Insert(users);\r\n                Assert.Equal(total, numberOfEntities);\r\n                users = connection.GetAll<User>().ToList();\r\n                Assert.Equal(users.Count, numberOfEntities);\r\n                var iusers = connection.GetAll<IUser>().ToList();\r\n                Assert.Equal(iusers.Count, numberOfEntities);\r\n                for (var i = 0; i < numberOfEntities; i++)\r\n                    Assert.Equal(iusers[i].Age, i);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Test for issue #933\r\n        /// </summary>\r\n        [Fact]\r\n        public void GetAndGetAllWithNullableValues()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id1 = connection.Insert(new NullableDate { DateValue = new DateTime(2011, 07, 14) });\r\n                var id2 = connection.Insert(new NullableDate { DateValue = null });\r\n\r\n                var value1 = connection.Get<INullableDate>(id1);\r\n                Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value);\r\n\r\n                var value2 = connection.Get<INullableDate>(id2);\r\n                Assert.True(value2.DateValue == null);\r\n\r\n                var value3 = connection.GetAll<INullableDate>().ToList();\r\n                Assert.Equal(new DateTime(2011, 07, 14), value3[0].DateValue.Value);\r\n                Assert.True(value3[1].DateValue == null);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void Transactions()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id = connection.Insert(new Car { Name = \"one car\" });   //insert outside transaction\r\n\r\n                var tran = connection.BeginTransaction();\r\n                var car = connection.Get<Car>(id, tran);\r\n                var orgName = car.Name;\r\n                car.Name = \"Another car\";\r\n                connection.Update(car, tran);\r\n                tran.Rollback();\r\n\r\n                car = connection.Get<Car>(id);  //updates should have been rolled back\r\n                Assert.Equal(car.Name, orgName);\r\n            }\r\n        }\r\n#if TRANSCOPE\r\n        [Fact]\r\n        public void TransactionScope()\r\n        {\r\n            using (var txscope = new TransactionScope())\r\n            {\r\n                using (var connection = GetOpenConnection())\r\n                {\r\n                    var id = connection.Insert(new Car { Name = \"one car\" });   //inser car within transaction\r\n\r\n                    txscope.Dispose();  //rollback\r\n\r\n                    Assert.Null(connection.Get<Car>(id));   //returns null - car with that id should not exist\r\n                }\r\n            }\r\n        }\r\n#endif\r\n\r\n        [Fact]\r\n        public void InsertCheckKey()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                Assert.Null(connection.Get<IUser>(3));\r\n                User user = new User { Name = \"Adamb\", Age = 10 };\r\n                int id = (int)connection.Insert(user);\r\n                Assert.Equal(user.Id, id);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void BuilderSelectClause()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var rand = new Random(8675309);\r\n                var data = new List<User>(100);\r\n                for (int i = 0; i < 100; i++)\r\n                {\r\n                    var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };\r\n                    data.Add(nU);\r\n                    nU.Id = (int)connection.Insert(nU);\r\n                }\r\n\r\n                var builder = new SqlBuilder();\r\n                var justId = builder.AddTemplate(\"SELECT /**select**/ FROM Users\");\r\n                var all = builder.AddTemplate(\"SELECT Name, /**select**/, Age FROM Users\");\r\n\r\n                builder.Select(\"Id\");\r\n\r\n                var ids = connection.Query<int>(justId.RawSql, justId.Parameters);\r\n                var users = connection.Query<User>(all.RawSql, all.Parameters);\r\n\r\n                foreach (var u in data)\r\n                {\r\n                    if (!ids.Any(i => u.Id == i)) throw new Exception(\"Missing ids in select\");\r\n                    if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) throw new Exception(\"Missing users in select\");\r\n                }\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void BuilderTemplateWithoutComposition()\r\n        {\r\n            var builder = new SqlBuilder();\r\n            var template = builder.AddTemplate(\"SELECT COUNT(*) FROM Users WHERE Age = @age\", new { age = 5 });\r\n\r\n            if (template.RawSql == null) throw new Exception(\"RawSql null\");\r\n            if (template.Parameters == null) throw new Exception(\"Parameters null\");\r\n\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n                connection.Insert(new User { Age = 5, Name = \"Testy McTestington\" });\r\n\r\n                if (connection.Query<int>(template.RawSql, template.Parameters).Single() != 1)\r\n                    throw new Exception(\"Query failed\");\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void InsertFieldWithReservedName()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                connection.DeleteAll<User>();\r\n                var id = connection.Insert(new Result() { Name = \"Adam\", Order = 1 });\r\n\r\n                var result = connection.Get<Result>(id);\r\n                Assert.Equal(1, result.Order);\r\n            }\r\n        }\r\n\r\n        [Fact]\r\n        public void DeleteAll()\r\n        {\r\n            using (var connection = GetOpenConnection())\r\n            {\r\n                var id1 = connection.Insert(new User { Name = \"Alice\", Age = 32 });\r\n                var id2 = connection.Insert(new User { Name = \"Bob\", Age = 33 });\r\n                Assert.True(connection.DeleteAll<User>());\r\n                Assert.Null(connection.Get<User>(id1));\r\n                Assert.Null(connection.Get<User>(id2));\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/TestSuites.cs",
    "content": "﻿using Microsoft.Data.Sqlite;\r\nusing MySqlConnector;\r\nusing System;\r\nusing System.Data;\r\nusing System.Data.SqlClient;\r\nusing System.IO;\r\nusing Xunit;\r\nusing Xunit.Sdk;\r\n\r\nnamespace Dapper.Tests.Contrib\r\n{\r\n    // The test suites here implement TestSuiteBase so that each provider runs\r\n    // the entire set of tests without declarations per method\r\n    // If we want to support a new provider, they need only be added here - not in multiple places\r\n\r\n    [XunitTestCaseDiscoverer(\"Dapper.Tests.SkippableFactDiscoverer\", \"Dapper.Tests.Contrib\")]\r\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\r\n    public class SkippableFactAttribute : FactAttribute\r\n    {\r\n    }\r\n\r\n    public class SqlServerTestSuite : TestSuite\r\n    {\r\n        private const string DbName = \"tempdb\";\r\n        public static string ConnectionString =>\r\n            GetConnectionString(\"SqlServerConnectionString\", $\"Data Source=.;Initial Catalog={DbName};Integrated Security=True\");\r\n\r\n        public override IDbConnection GetConnection() => new SqlConnection(ConnectionString);\r\n\r\n        static SqlServerTestSuite()\r\n        {\r\n            using (var connection = new SqlConnection(ConnectionString))\r\n            {\r\n                // ReSharper disable once AccessToDisposedClosure\r\n                void dropTable(string name) => connection.Execute($\"IF OBJECT_ID('{name}', 'U') IS NOT NULL DROP TABLE [{name}]; \");\r\n                connection.Open();\r\n                dropTable(\"Stuff\");\r\n                connection.Execute(\"CREATE TABLE Stuff (TheId int IDENTITY(1,1) not null, Name nvarchar(100) not null, Created DateTime null);\");\r\n                dropTable(\"People\");\r\n                connection.Execute(\"CREATE TABLE People (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"Users\");\r\n                connection.Execute(\"CREATE TABLE Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null);\");\r\n                dropTable(\"Automobiles\");\r\n                connection.Execute(\"CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"Results\");\r\n                connection.Execute(\"CREATE TABLE Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null);\");\r\n                dropTable(\"ObjectX\");\r\n                connection.Execute(\"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"ObjectY\");\r\n                connection.Execute(\"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"ObjectZ\");\r\n                connection.Execute(\"CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"GenericType\");\r\n                connection.Execute(\"CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null);\");\r\n                dropTable(\"NullableDates\");\r\n                connection.Execute(\"CREATE TABLE NullableDates (Id int IDENTITY(1,1) not null, DateValue DateTime null);\");\r\n            }\r\n        }\r\n    }\r\n\r\n    public class MySqlServerTestSuite : TestSuite\r\n    {\r\n        public static string ConnectionString { get; } =\r\n            GetConnectionString(\"MySqlConnectionString\", \"Server=localhost;Database=tests;Uid=test;Pwd=pass;UseAffectedRows=false;\");\r\n\r\n        public override IDbConnection GetConnection()\r\n        {\r\n            if (_skip) Skip.Inconclusive(\"Skipping MySQL Tests - no server.\");\r\n            return new MySqlConnection(ConnectionString);\r\n        }\r\n\r\n        private static readonly bool _skip;\r\n\r\n        static MySqlServerTestSuite()\r\n        {\r\n            try\r\n            {\r\n                using (var connection = new MySqlConnection(ConnectionString))\r\n                {\r\n                    // ReSharper disable once AccessToDisposedClosure\r\n                    void dropTable(string name) => connection.Execute($\"DROP TABLE IF EXISTS `{name}`;\");\r\n                    connection.Open();\r\n                    dropTable(\"Stuff\");\r\n                    connection.Execute(\"CREATE TABLE Stuff (TheId int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, Created DateTime null);\");\r\n                    dropTable(\"People\");\r\n                    connection.Execute(\"CREATE TABLE People (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null);\");\r\n                    dropTable(\"Users\");\r\n                    connection.Execute(\"CREATE TABLE Users (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, Age int not null);\");\r\n                    dropTable(\"Automobiles\");\r\n                    connection.Execute(\"CREATE TABLE Automobiles (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null);\");\r\n                    dropTable(\"Results\");\r\n                    connection.Execute(\"CREATE TABLE Results (Id int not null AUTO_INCREMENT PRIMARY KEY, Name nvarchar(100) not null, `Order` int not null);\");\r\n                    dropTable(\"ObjectX\");\r\n                    connection.Execute(\"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null);\");\r\n                    dropTable(\"ObjectY\");\r\n                    connection.Execute(\"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null);\");\r\n                    dropTable(\"ObjectZ\");\r\n                    connection.Execute(\"CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null);\");\r\n                    dropTable(\"GenericType\");\r\n                    connection.Execute(\"CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null);\");\r\n                    dropTable(\"NullableDates\");\r\n                    connection.Execute(\"CREATE TABLE NullableDates (Id int not null AUTO_INCREMENT PRIMARY KEY, DateValue DateTime);\");\r\n                }\r\n            }\r\n            catch (MySqlException e)\r\n            {\r\n                if (e.Message.Contains(\"Unable to connect\"))\r\n                    _skip = true;\r\n                else\r\n                    throw;\r\n            }\r\n        }\r\n    }\r\n\r\n    public class SQLiteTestSuite : TestSuite\r\n    {\r\n        private const string FileName = \"Test.DB.sqlite\";\r\n        public static string ConnectionString => $\"Filename=./{FileName};Mode=ReadWriteCreate;\";\r\n        public override IDbConnection GetConnection() => new SqliteConnection(ConnectionString);\r\n\r\n        static SQLiteTestSuite()\r\n        {\r\n            if (File.Exists(FileName))\r\n            {\r\n                File.Delete(FileName);\r\n            }\r\n            using (var connection = new SqliteConnection(ConnectionString))\r\n            {\r\n                connection.Open();\r\n                connection.Execute(\"CREATE TABLE Stuff (TheId integer primary key autoincrement not null, Name nvarchar(100) not null, Created DateTime null) \");\r\n                connection.Execute(\"CREATE TABLE People (Id integer primary key autoincrement not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE Users (Id integer primary key autoincrement not null, Name nvarchar(100) not null, Age int not null) \");\r\n                connection.Execute(\"CREATE TABLE Automobiles (Id integer primary key autoincrement not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE Results (Id integer primary key autoincrement not null, Name nvarchar(100) not null, [Order] int not null) \");\r\n                connection.Execute(\"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE ObjectY (ObjectYId integer not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE ObjectZ (Id integer not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(\"CREATE TABLE NullableDates (Id integer primary key autoincrement not null, DateValue DateTime) \");\r\n            }\r\n        }\r\n    }\r\n\r\n\r\n#if SQLCE\r\n    public class SqlCETestSuite : TestSuite\r\n    {\r\n        const string FileName = \"Test.DB.sdf\";\r\n        public static string ConnectionString => $\"Data Source={FileName};\";\r\n        public override IDbConnection GetConnection() => new SqlCeConnection(ConnectionString);\r\n            \r\n        static SqlCETestSuite()\r\n        {\r\n            if (File.Exists(FileName))\r\n            {\r\n                File.Delete(FileName);\r\n            }\r\n            var engine = new SqlCeEngine(ConnectionString);\r\n            engine.CreateDatabase();\r\n            using (var connection = new SqlCeConnection(ConnectionString))\r\n            {\r\n                connection.Open();\r\n                connection.Execute(@\"CREATE TABLE Stuff (TheId int IDENTITY(1,1) not null, Name nvarchar(100) not null, Created DateTime null) \");\r\n                connection.Execute(@\"CREATE TABLE People (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) \");\r\n                connection.Execute(@\"CREATE TABLE Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) \");\r\n                connection.Execute(@\"CREATE TABLE ObjectX (ObjectXId nvarchar(100) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null) \");\r\n                connection.Execute(@\"CREATE TABLE NullableDates (Id int IDENTITY(1,1) not null, DateValue DateTime null) \");\r\n            }\r\n            Console.WriteLine(\"Created database\");\r\n        }\r\n    }\r\n#endif\r\n}\r\n"
  },
  {
    "path": "tests/Dapper.Tests.Contrib/xunit.runner.json",
    "content": "﻿{\n  \"$schema\": \"https://xunit.net/schema/current/xunit.runner.schema.json\",\n  \"shadowCopy\": false\n}"
  },
  {
    "path": "tests/Directory.Build.props",
    "content": "<Project>\n  <Import Project=\"$(MSBuildThisFileDirectory)..\\Directory.Build.props\" />\n  <PropertyGroup>\n    <OutputType>Library</OutputType>\n    <SignAssembly>false</SignAssembly>\n    <GenerateDocumentationFile>false</GenerateDocumentationFile>\n    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>\n    <IsPackable>false</IsPackable>\n    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n\n    <DebugType>Full</DebugType>\n    <DefineConstants Condition=\"'$(OS)' == 'Windows_NT'\">$(DefineConstants);WINDOWS</DefineConstants>\n  </PropertyGroup>\n  \n  <ItemGroup>\n    <ProjectReference Include=\"../../src/Dapper.Contrib/Dapper.Contrib.csproj\" />\n    <PackageReference Include=\"GitHubActionsTestLogger\" Version=\"1.1.2\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"16.8.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.1\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.3\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net461'\">\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Configuration\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Data.Linq\" />\n    <Reference Include=\"System.Runtime.Caching\" />\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.Transactions\" />\n    <Reference Include=\"System.Web\" />\n    <Reference Include=\"System.Web.Extensions\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- <Content Include=\"xunit.runner.json\" Condition=\"'$(OS)' == 'Unix'\" CopyToOutputDirectory=\"PreserveNewest\" /> -->\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "tests/Directory.Build.targets",
    "content": "<Project>\n  <PropertyGroup>\n    <IsTestProject Condition=\"'$(OS)' != 'Windows_NT' AND $(TargetFramework.StartsWith('net4'))\">false</IsTestProject>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "tests/docker-compose.yml",
    "content": "version: \"3\"\nservices:\n  mysql:\n    image: mysql:8\n    container_name: mysql\n    ports:\n      - 3306:3306\n    environment:\n      MYSQL_ROOT_PASSWORD: root\n      MYSQL_DATABASE: test\n  postgres:\n    image: postgres:alpine\n    container_name: postgres\n    ports:\n      - 5432:5432\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: test\n  sqlserver:\n    image: mcr.microsoft.com/mssql/server:2019-latest\n    container_name: sql-server-db\n    ports:\n      - 1433:1433\n    environment:\n      ACCEPT_EULA: Y\n      SA_PASSWORD: \"Password.\"\n"
  },
  {
    "path": "version.json",
    "content": "{\n  \"version\": \"2.0\",\n  \"assemblyVersion\": \"2.0.0.0\",\n  \"publicReleaseRefSpec\": [\n    \"^refs/heads/main$\",\n    \"^refs/tags/v\\\\d+\\\\.\\\\d+\"\n  ],\n  \"nugetPackageVersion\": {\n    \"semVer\": 2\n  },\n  \"cloudBuild\": {\n    \"buildNumber\": {\n      \"enabled\": true,\n      \"setVersionVariables\": true\n    }\n  }\n}"
  }
]